Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sot/source/sdstor/stgelem.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 <string.h>
22
23
#include <o3tl/safeint.hxx>
24
#include <rtl/ustring.hxx>
25
#include <com/sun/star/lang/Locale.hpp>
26
#include <unotools/charclass.hxx>
27
#include <sot/stg.hxx>
28
#include "stgelem.hxx"
29
#include "stgio.hxx"
30
31
const sal_uInt16 nMaxLegalStr = 31;
32
33
const sal_uInt8 cStgSignature[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 };
34
35
////////////////////////////// struct ClsId
36
37
SvStream& ReadClsId( SvStream& r, ClsId& rId )
38
2.74M
{
39
2.74M
    r.ReadUInt32( rId.Data1 )
40
2.74M
     .ReadUInt16( rId.Data2 )
41
2.74M
     .ReadUInt16( rId.Data3 )
42
2.74M
     .ReadUChar( rId.Data4[0] )
43
2.74M
     .ReadUChar( rId.Data4[1] )
44
2.74M
     .ReadUChar( rId.Data4[2] )
45
2.74M
     .ReadUChar( rId.Data4[3] )
46
2.74M
     .ReadUChar( rId.Data4[4] )
47
2.74M
     .ReadUChar( rId.Data4[5] )
48
2.74M
     .ReadUChar( rId.Data4[6] )
49
2.74M
     .ReadUChar( rId.Data4[7] );
50
2.74M
    return r;
51
2.74M
}
52
53
SvStream& WriteClsId( SvStream& r, const ClsId& rId )
54
1.83M
{
55
1.83M
    return
56
1.83M
       r .WriteUInt32( rId.Data1 )
57
1.83M
         .WriteUInt16( rId.Data2 )
58
1.83M
         .WriteUInt16( rId.Data3 )
59
1.83M
         .WriteUChar( rId.Data4[0] )
60
1.83M
         .WriteUChar( rId.Data4[1] )
61
1.83M
         .WriteUChar( rId.Data4[2] )
62
1.83M
         .WriteUChar( rId.Data4[3] )
63
1.83M
         .WriteUChar( rId.Data4[4] )
64
1.83M
         .WriteUChar( rId.Data4[5] )
65
1.83M
         .WriteUChar( rId.Data4[6] )
66
1.83M
         .WriteUChar( rId.Data4[7] );
67
1.83M
}
68
69
///////////////////////////// class StgHeader
70
71
StgHeader::StgHeader()
72
220k
: m_nVersion( 0 )
73
220k
, m_nByteOrder( 0 )
74
220k
, m_nPageSize( 0 )
75
220k
, m_nDataPageSize( 0 )
76
220k
, m_bDirty( sal_uInt8(false) )
77
220k
, m_nFATSize( 0 )
78
220k
, m_nTOCstrm( 0 )
79
220k
, m_nReserved( 0 )
80
220k
, m_nThreshold( 0 )
81
220k
, m_nDataFAT( 0 )
82
220k
, m_nDataFATSize( 0 )
83
220k
, m_nMasterChain( 0 )
84
220k
, m_nMaster( 0 )
85
220k
{
86
220k
}
87
88
void StgHeader::Init()
89
78.8k
{
90
78.8k
    memcpy( m_cSignature, cStgSignature, 8 );
91
78.8k
    memset( &m_aClsId, 0, sizeof( ClsId ) );
92
78.8k
    m_nVersion      = 0x0003003B;
93
78.8k
    m_nByteOrder    = 0xFFFE;
94
78.8k
    m_nPageSize     = 9;          // 512 bytes
95
78.8k
    m_nDataPageSize = 6;          // 64 bytes
96
78.8k
    m_bDirty = sal_uInt8(false);
97
78.8k
    memset( m_cReserved, 0, sizeof( m_cReserved ) );
98
78.8k
    m_nFATSize = 0;
99
78.8k
    m_nTOCstrm = 0;
100
78.8k
    m_nReserved = 0;
101
78.8k
    m_nThreshold    = 4096;
102
78.8k
    m_nDataFAT = 0;
103
78.8k
    m_nDataFATSize  = 0;
104
78.8k
    m_nMasterChain  = STG_EOF;
105
106
78.8k
    SetTOCStart( STG_EOF );
107
78.8k
    SetDataFATStart( STG_EOF );
108
8.67M
    for( short i = 0; i < cFATPagesInHeader; i++ )
109
8.59M
        SetFATPage( i, STG_FREE );
110
78.8k
}
111
112
bool StgHeader::Load( StgIo& rIo )
113
127k
{
114
127k
    bool bResult = false;
115
127k
    if ( rIo.GetStrm() )
116
127k
    {
117
127k
        SvStream& r = *rIo.GetStrm();
118
127k
        bResult = Load( r );
119
127k
        bResult = ( bResult && rIo.Good() );
120
127k
    }
121
122
127k
    return bResult;
123
127k
}
124
125
bool StgHeader::Load( SvStream& r )
126
141k
{
127
141k
    r.Seek( 0 );
128
141k
    r.ReadBytes( m_cSignature, 8 );
129
141k
    ReadClsId( r, m_aClsId );         // 08 Class ID
130
141k
    r.ReadInt32( m_nVersion )                   // 1A version number
131
141k
     .ReadUInt16( m_nByteOrder )                 // 1C Unicode byte order indicator
132
141k
     .ReadInt16( m_nPageSize )                  // 1E 1 << nPageSize = block size
133
141k
     .ReadInt16( m_nDataPageSize );             // 20 1 << this size == data block size
134
141k
    if (!r.good())
135
1.75k
        return false;
136
139k
    if (!checkSeek(r, r.Tell() + 10))
137
233
        return false;
138
139k
    r.ReadInt32( m_nFATSize )                   // 2C total number of FAT pages
139
139k
     .ReadInt32( m_nTOCstrm )                   // 30 starting page for the TOC stream
140
139k
     .ReadInt32( m_nReserved )                  // 34
141
139k
     .ReadInt32( m_nThreshold )                 // 38 minimum file size for big data
142
139k
     .ReadInt32( m_nDataFAT )                   // 3C page # of 1st data FAT block
143
139k
     .ReadInt32( m_nDataFATSize )               // 40 # of data FATpages
144
139k
     .ReadInt32( m_nMasterChain )               // 44 chain to the next master block
145
139k
     .ReadInt32( m_nMaster );                   // 48 # of additional master blocks
146
139k
    for(sal_Int32 & i : m_nMasterFAT)
147
15.2M
        r.ReadInt32( i );
148
149
139k
    return r.good() && Check();
150
139k
}
151
152
bool StgHeader::Store( StgIo& rIo )
153
155k
{
154
155k
    if( !m_bDirty )
155
0
        return true;
156
157
155k
    SvStream& r = *rIo.GetStrm();
158
155k
    r.Seek( 0 );
159
155k
    r.WriteBytes( m_cSignature, 8 );
160
155k
    WriteClsId( r, m_aClsId );                   // 08 Class ID
161
155k
    r.WriteInt32( m_nVersion )                   // 1A version number
162
155k
     .WriteUInt16( m_nByteOrder )                 // 1C Unicode byte order indicator
163
155k
     .WriteInt16( m_nPageSize )                  // 1E 1 << nPageSize = block size
164
155k
     .WriteInt16( m_nDataPageSize )              // 20 1 << this size == data block size
165
155k
     .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt16( 0 )
166
155k
     .WriteInt32( m_nFATSize )                   // 2C total number of FAT pages
167
155k
     .WriteInt32( m_nTOCstrm )                   // 30 starting page for the TOC stream
168
155k
     .WriteInt32( m_nReserved )                  // 34
169
155k
     .WriteInt32( m_nThreshold )                 // 38 minimum file size for big data
170
155k
     .WriteInt32( m_nDataFAT )                   // 3C page # of 1st data FAT block
171
155k
     .WriteInt32( m_nDataFATSize )               // 40 # of data FAT pages
172
155k
     .WriteInt32( m_nMasterChain )               // 44 chain to the next master block
173
155k
     .WriteInt32( m_nMaster );                   // 48 # of additional master blocks
174
155k
    for(sal_Int32 i : m_nMasterFAT)
175
16.9M
        r.WriteInt32( i );
176
155k
    m_bDirty = sal_uInt8(!rIo.Good());
177
155k
    return !m_bDirty;
178
155k
}
179
180
static bool lcl_wontoverflow(short shift)
181
503k
{
182
503k
    return shift >= 0 && shift < short(sizeof(short)) * 8 - 1;
183
503k
}
184
185
static bool isKnownSpecial(sal_Int32 nLocation)
186
1.14M
{
187
1.14M
    return (nLocation == STG_FREE ||
188
1.11M
            nLocation == STG_EOF ||
189
199k
            nLocation == STG_FAT ||
190
188k
            nLocation == STG_MASTER);
191
1.14M
}
192
193
// Perform thorough checks also on unknown variables
194
bool StgHeader::Check()
195
259k
{
196
259k
    return  memcmp( m_cSignature, cStgSignature, 8 ) == 0
197
251k
            && static_cast<short>( m_nVersion >> 16 ) == 3
198
251k
            && m_nPageSize == 9
199
251k
            && lcl_wontoverflow(m_nPageSize)
200
251k
            && lcl_wontoverflow(m_nDataPageSize)
201
251k
            && m_nFATSize > 0
202
251k
            && m_nTOCstrm >= 0
203
251k
            && m_nThreshold > 0
204
251k
            && ( isKnownSpecial(m_nDataFAT) || ( m_nDataFAT >= 0 && m_nDataFATSize > 0 ) )
205
251k
            && ( isKnownSpecial(m_nMasterChain) || m_nMasterChain >=0 )
206
250k
            && m_nMaster >= 0;
207
259k
}
208
209
sal_Int32 StgHeader::GetFATPage( short n ) const
210
11.4M
{
211
11.4M
    if( n >= 0 && n < cFATPagesInHeader )
212
11.4M
        return m_nMasterFAT[ n ];
213
0
    else
214
0
        return STG_EOF;
215
11.4M
}
216
217
void StgHeader::SetFATPage( short n, sal_Int32 nb )
218
8.69M
{
219
8.69M
    if( n >= 0 && n < cFATPagesInHeader )
220
8.69M
    {
221
8.69M
        if( m_nMasterFAT[ n ] != nb )
222
8.69M
        {
223
8.69M
            m_bDirty = sal_uInt8(true);
224
8.69M
            m_nMasterFAT[ n ] = nb;
225
8.69M
        }
226
8.69M
    }
227
8.69M
}
228
229
void StgHeader::SetTOCStart( sal_Int32 n )
230
390k
{
231
390k
    if( n != m_nTOCstrm )
232
234k
    {
233
234k
        m_bDirty = sal_uInt8(true);
234
234k
        m_nTOCstrm = n;
235
234k
    }
236
390k
}
237
238
void StgHeader::SetDataFATStart( sal_Int32 n )
239
234k
{
240
234k
    if( n != m_nDataFAT )
241
119k
    {
242
119k
        m_bDirty = sal_uInt8(true);
243
119k
        m_nDataFAT = n;
244
119k
    }
245
234k
}
246
247
void StgHeader::SetDataFATSize( sal_Int32 n )
248
155k
{
249
155k
    if( n != m_nDataFATSize )
250
40.1k
    {
251
40.1k
        m_bDirty = sal_uInt8(true);
252
40.1k
        m_nDataFATSize = n;
253
40.1k
    }
254
155k
}
255
256
void StgHeader::SetFATSize( sal_Int32 n )
257
91.2k
{
258
91.2k
    if( n != m_nFATSize )
259
91.2k
    {
260
91.2k
        m_bDirty = sal_uInt8(true);
261
91.2k
        m_nFATSize = n;
262
91.2k
    }
263
91.2k
}
264
265
void StgHeader::SetFATChain( sal_Int32 n )
266
8
{
267
8
    if( n != m_nMasterChain )
268
8
    {
269
8
        m_bDirty = sal_uInt8(true);
270
8
        m_nMasterChain = n;
271
8
    }
272
8
}
273
274
void StgHeader::SetMasters( sal_Int32 n )
275
8
{
276
8
    if( n != m_nMaster )
277
8
    {
278
8
        m_bDirty = sal_uInt8(true);
279
8
        m_nMaster = n;
280
8
    }
281
8
}
282
283
///////////////////////////// class StgEntry
284
285
void StgEntry::Init()
286
7.57M
{
287
7.57M
    memset( m_nName, 0, sizeof( m_nName ) );
288
7.57M
    m_nNameLen = 0;
289
7.57M
    m_cType = 0;
290
7.57M
    m_cFlags = 0;
291
7.57M
    m_nLeft = 0;
292
7.57M
    m_nRight = 0;
293
7.57M
    m_nChild = 0;
294
7.57M
    memset( &m_aClsId, 0, sizeof( m_aClsId ) );
295
7.57M
    m_nFlags = 0;
296
7.57M
    m_nMtime[0] = 0; m_nMtime[1] = 0;
297
7.57M
    m_nAtime[0] = 0; m_nAtime[1] = 0;
298
7.57M
    m_nPage1 = 0;
299
7.57M
    m_nSize = 0;
300
7.57M
    m_nUnknown = 0;
301
302
7.57M
    SetLeaf( STG_LEFT,  STG_FREE );
303
7.57M
    SetLeaf( STG_RIGHT, STG_FREE );
304
7.57M
    SetLeaf( STG_CHILD, STG_FREE );
305
7.57M
    SetLeaf( STG_DATA,  STG_EOF );
306
7.57M
}
307
308
static OUString ToUpperUnicode( const OUString & rStr )
309
10.0M
{
310
    // I don't know the locale, so en_US is hopefully fine
311
10.0M
    static CharClass aCC( LanguageTag( css::lang::Locale( u"en"_ustr, u"US"_ustr, u""_ustr )) );
312
10.0M
    return aCC.uppercase( rStr );
313
10.0M
}
314
315
void StgEntry::SetName( const OUString& rName )
316
7.41M
{
317
    // I don't know the locale, so en_US is hopefully fine
318
7.41M
    m_aName = ToUpperUnicode( rName );
319
7.41M
    if(m_aName.getLength() > nMaxLegalStr)
320
78.1k
    {
321
78.1k
        m_aName = m_aName.copy(0, nMaxLegalStr);
322
78.1k
    }
323
324
7.41M
    sal_Int32 i;
325
67.9M
    for( i = 0; i < rName.getLength() && i <= nMaxLegalStr; i++ )
326
60.5M
    {
327
60.5M
        m_nName[ i ] = rName[ i ];
328
60.5M
    }
329
184M
    while (i <= nMaxLegalStr)
330
176M
    {
331
176M
        m_nName[ i++ ] = 0;
332
176M
    }
333
7.41M
    m_nNameLen = ( rName.getLength() + 1 ) << 1;
334
7.41M
}
335
336
sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const
337
16.6M
{
338
16.6M
    sal_Int32 n = -1;
339
16.6M
    switch( eRef )
340
16.6M
    {
341
2.59M
    case STG_LEFT:  n = m_nLeft;  break;
342
2.59M
    case STG_RIGHT: n = m_nRight; break;
343
8.74M
    case STG_CHILD: n = m_nChild; break;
344
2.72M
    case STG_DATA:  n = m_nPage1; break;
345
16.6M
    }
346
16.6M
    return n;
347
16.6M
}
348
349
void StgEntry::SetLeaf( StgEntryRef eRef, sal_Int32 nPage )
350
36.3M
{
351
36.3M
    switch( eRef )
352
36.3M
    {
353
8.87M
    case STG_LEFT:  m_nLeft  = nPage; break;
354
8.87M
    case STG_RIGHT: m_nRight = nPage; break;
355
8.87M
    case STG_CHILD: m_nChild = nPage; break;
356
9.67M
    case STG_DATA:  m_nPage1 = nPage; break;
357
36.3M
    }
358
36.3M
}
359
360
void StgEntry::SetClassId( const ClsId& r )
361
528k
{
362
528k
    memcpy( &m_aClsId, &r, sizeof( ClsId ) );
363
528k
}
364
365
void StgEntry::GetName( OUString& rName ) const
366
4.06M
{
367
4.06M
    sal_uInt16 n = m_nNameLen;
368
4.06M
    if( n )
369
4.05M
        n = ( n >> 1 ) - 1;
370
4.06M
    rName = OUString(m_nName, n);
371
4.06M
}
372
373
// Compare two entries. Do this case-insensitive.
374
375
sal_Int32 StgEntry::Compare( const StgEntry& r ) const
376
33.4M
{
377
33.4M
    if (r.m_nNameLen != m_nNameLen)
378
21.4M
        return r.m_nNameLen > m_nNameLen ? 1 : -1;
379
12.0M
    else
380
12.0M
        return r.m_aName.compareTo(m_aName);
381
33.4M
}
382
383
// These load/store operations are a bit more complicated,
384
// since they have to copy their contents into a packed structure.
385
386
bool StgEntry::Load(const void* pFrom, sal_uInt32 nBufSize, sal_uInt64 nUnderlyingStreamSize)
387
2.60M
{
388
2.60M
    if ( nBufSize < 128 )
389
0
        return false;
390
391
2.60M
    SvMemoryStream r( const_cast<void *>(pFrom), nBufSize, StreamMode::READ );
392
2.60M
    for(sal_Unicode & i : m_nName)
393
83.3M
        r.ReadUtf16( i );             // 00 name as WCHAR
394
2.60M
    r.ReadUInt16( m_nNameLen )                   // 40 size of name in bytes including 00H
395
2.60M
     .ReadUChar( m_cType )                      // 42 entry type
396
2.60M
     .ReadUChar( m_cFlags )                     // 43 0 or 1 (tree balance?)
397
2.60M
     .ReadInt32( m_nLeft )                      // 44 left node entry
398
2.60M
     .ReadInt32( m_nRight )                     // 48 right node entry
399
2.60M
     .ReadInt32( m_nChild );                    // 4C 1st child entry if storage
400
2.60M
    ReadClsId( r, m_aClsId );         // 50 class ID (optional)
401
2.60M
    r.ReadInt32( m_nFlags )                     // 60 state flags(?)
402
2.60M
     .ReadInt32( m_nMtime[ 0 ] )                // 64 modification time
403
2.60M
     .ReadInt32( m_nMtime[ 1 ] )                // 64 modification time
404
2.60M
     .ReadInt32( m_nAtime[ 0 ] )                // 6C creation and access time
405
2.60M
     .ReadInt32( m_nAtime[ 1 ] )                // 6C creation and access time
406
2.60M
     .ReadInt32( m_nPage1 )                     // 74 starting block (either direct or translated)
407
2.60M
     .ReadInt32( m_nSize )                      // 78 file size
408
2.60M
     .ReadInt32( m_nUnknown );                  // 7C unknown
409
410
2.60M
    sal_uInt16 n = m_nNameLen;
411
2.60M
    if( n )
412
2.50M
        n = ( n >> 1 ) - 1;
413
414
2.60M
    if (n > nMaxLegalStr)
415
3.17k
        return false;
416
417
2.60M
    if (m_cType != STG_STORAGE)
418
1.98M
    {
419
1.98M
        if (m_nPage1 < 0 && !isKnownSpecial(m_nPage1))
420
2.23k
        {
421
            //bad pageid
422
2.23k
            return false;
423
2.23k
        }
424
1.98M
        if (m_cType == STG_EMPTY)
425
284k
        {
426
            /*
427
             tdf#112399 opens fine in MSOffice 2013 despite a massive m_nSize field
428
429
             Free (unused) directory entries are marked with Object Type 0x0
430
             (unknown or unallocated). The entire directory entry must consist of
431
             all zeroes except for the child, right sibling, and left sibling
432
             pointers, which must be initialized to NOSTREAM (0xFFFFFFFF).
433
            */
434
284k
            m_nSize = 0;
435
284k
        }
436
1.98M
        if (m_nSize < 0)
437
1.97k
        {
438
            // the size makes no sense for the substorage
439
            // TODO/LATER: actually the size should be an unsigned value, but
440
            // in this case it would mean a stream of more than 2Gb
441
1.97k
            return false;
442
1.97k
        }
443
1.98M
        if (o3tl::make_unsigned(m_nSize) > nUnderlyingStreamSize)
444
1.86k
        {
445
            // surely an entry cannot be larger than the underlying file
446
1.86k
            return false;
447
1.86k
        }
448
449
1.98M
    }
450
451
2.59M
    m_aName = OUString(m_nName , n);
452
    // I don't know the locale, so en_US is hopefully fine
453
2.59M
    m_aName = ToUpperUnicode( m_aName );
454
2.59M
    if(m_aName.getLength() > nMaxLegalStr)
455
18.5k
    {
456
18.5k
        m_aName = m_aName.copy(0, nMaxLegalStr);
457
18.5k
    }
458
459
2.59M
    return true;
460
2.60M
}
461
462
void StgEntry::Store( void* pTo )
463
1.67M
{
464
1.67M
    SvMemoryStream r( pTo, 128, StreamMode::WRITE );
465
1.67M
    for(sal_Unicode i : m_nName)
466
53.7M
        r.WriteUInt16( i );            // 00 name as WCHAR
467
1.67M
    r.WriteUInt16( m_nNameLen )                   // 40 size of name in bytes including 00H
468
1.67M
     .WriteUChar( m_cType )                      // 42 entry type
469
1.67M
     .WriteUChar( m_cFlags )                     // 43 0 or 1 (tree balance?)
470
1.67M
     .WriteInt32( m_nLeft )                      // 44 left node entry
471
1.67M
     .WriteInt32( m_nRight )                     // 48 right node entry
472
1.67M
     .WriteInt32( m_nChild );                    // 4C 1st child entry if storage;
473
1.67M
    WriteClsId( r, m_aClsId );                   // 50 class ID (optional)
474
1.67M
    r.WriteInt32( m_nFlags )                     // 60 state flags(?)
475
1.67M
     .WriteInt32( m_nMtime[ 0 ] )                // 64 modification time
476
1.67M
     .WriteInt32( m_nMtime[ 1 ] )                // 64 modification time
477
1.67M
     .WriteInt32( m_nAtime[ 0 ] )                // 6C creation and access time
478
1.67M
     .WriteInt32( m_nAtime[ 1 ] )                // 6C creation and access time
479
1.67M
     .WriteInt32( m_nPage1 )                     // 74 starting block (either direct or translated)
480
1.67M
     .WriteInt32( m_nSize )                      // 78 file size
481
1.67M
     .WriteInt32( m_nUnknown );                  // 7C unknown
482
1.67M
}
483
484
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */