1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "stgelem.hxx"
22 #include "stgcache.hxx"
23 #include "stgstrms.hxx"
26 #include <o3tl/safeint.hxx>
27 #include <sal/log.hxx>
32 ///////////////////////////// class StgIo
34 // This class holds the storage header and all internal streams.
40 m_pDataStrm
= nullptr;
53 // Load the header. Do not set an error code if the header is invalid.
59 if( m_aHdr
.Load( *this ) )
72 // Set up an initial, empty storage
81 void StgIo::SetupStreams()
89 m_pDataStrm
= nullptr;
93 short nPhysPageSize
= 1 << m_aHdr
.GetPageSize();
94 SetPhysPageSize(nPhysPageSize
);
95 sal_Int32 nFatStrmSize
;
96 if (o3tl::checked_multiply
<sal_Int32
>(m_aHdr
.GetFATSize(), nPhysPageSize
, nFatStrmSize
))
98 SAL_WARN("sot", "Error: " << m_aHdr
.GetFATSize() << " * " << nPhysPageSize
<< " would overflow");
99 SetError(SVSTREAM_FILEFORMAT_ERROR
);
105 m_pFAT
= new StgFATStrm(*this, nFatStrmSize
);
106 m_pTOC
= new StgDirStrm(*this);
110 StgDirEntry
* pRoot
= m_pTOC
->GetRoot();
113 m_pDataFAT
= new StgDataStrm( *this, m_aHdr
.GetDataFATStart(), -1 );
114 m_pDataStrm
= new StgDataStrm( *this, *pRoot
);
115 m_pDataFAT
->SetIncrement( 1 << m_aHdr
.GetPageSize() );
116 m_pDataStrm
->SetIncrement( GetDataPageSize() );
117 m_pDataStrm
->SetEntry( *pRoot
);
120 SetError( SVSTREAM_FILEFORMAT_ERROR
);
123 // get the logical data page size
125 short StgIo::GetDataPageSize() const
127 return 1 << m_aHdr
.GetDataPageSize();
132 bool StgIo::CommitAll()
134 // Store the data (all streams and the TOC)
135 if( m_pTOC
&& m_pTOC
->Store() && m_pDataFAT
)
139 m_aHdr
.SetDataFATStart( m_pDataFAT
->GetStart() );
140 m_aHdr
.SetDataFATSize( m_pDataFAT
->GetPages() );
141 m_aHdr
.SetTOCStart( m_pTOC
->GetStart() );
142 if( m_aHdr
.Store( *this ) )
145 const ErrCode n
= GetStrm()->GetError();
148 if( n
==ERRCODE_NONE
) ValidateFATs();
150 return n
== ERRCODE_NONE
;
154 SetError( SVSTREAM_WRITE_ERROR
);
162 std::unique_ptr
<sal_Int32
[]> pFat
;
163 std::unique_ptr
<bool[]> pFree
;
168 EasyFat( StgIo
& rIo
, StgStrm
*pFatStream
, sal_Int32 nPSize
);
170 sal_Int32
GetPageSize() const { return nPageSize
; }
172 FatError
Mark( sal_Int32 nPage
, sal_Int32 nCount
, sal_Int32 nExpect
);
173 bool HasUnrefChains() const;
178 EasyFat::EasyFat( StgIo
& rIo
, StgStrm
* pFatStream
, sal_Int32 nPSize
)
179 : nPages(pFatStream
->GetSize() >> 2), nPageSize(nPSize
)
181 pFat
.reset( new sal_Int32
[ nPages
] );
182 pFree
.reset( new bool[ nPages
] );
184 rtl::Reference
< StgPage
> pPage
;
185 sal_Int32 nFatPageSize
= (1 << rIo
.m_aHdr
.GetPageSize()) - 2;
187 for( sal_Int32 nPage
= 0; nPage
< nPages
; nPage
++ )
189 if( ! (nPage
% nFatPageSize
) )
191 pFatStream
->Pos2Page( nPage
<< 2 );
192 sal_Int32 nPhysPage
= pFatStream
->GetPage();
193 pPage
= rIo
.Get( nPhysPage
, true );
196 pFat
[ nPage
] = StgCache::GetFromPage( pPage
, short( nPage
% nFatPageSize
) );
197 pFree
[ nPage
] = true;
201 bool EasyFat::HasUnrefChains() const
203 for( sal_Int32 nPage
= 0; nPage
< nPages
; nPage
++ )
205 if( pFree
[ nPage
] && pFat
[ nPage
] != -1 )
211 FatError
EasyFat::Mark( sal_Int32 nPage
, sal_Int32 nCount
, sal_Int32 nExpect
)
216 nCount
/= GetPageSize();
220 sal_Int32 nCurPage
= nPage
;
223 if( nCurPage
< 0 || nCurPage
>= nPages
)
224 return FatError::OutOfBounds
;
225 pFree
[ nCurPage
] = false;
226 nCurPage
= pFat
[ nCurPage
];
228 if( nCurPage
!= nExpect
&& nCount
== 1 )
229 return FatError::WrongLength
;
231 if( nCurPage
== nExpect
&& nCount
!= 1 && nCount
!= -1 )
232 return FatError::WrongLength
;
233 // last block for stream without length
234 if( nCurPage
== nExpect
&& nCount
== -1 )
253 FatError
ValidateMasterFATs();
254 FatError
ValidateDirectoryEntries();
255 FatError
FindUnrefedChains() const;
256 FatError
MarkAll( StgDirEntry
*pEntry
);
259 explicit Validator( StgIo
&rIo
);
260 bool IsError() const { return nError
!= FatError::Ok
; }
265 Validator::Validator( StgIo
&rIoP
)
266 : aSmallFat( rIoP
, rIoP
.m_pDataFAT
, 1 << rIoP
.m_aHdr
.GetDataPageSize() ),
267 aFat( rIoP
, rIoP
.m_pFAT
, 1 << rIoP
.m_aHdr
.GetPageSize() ),
270 FatError nErr
= nError
= FatError::Ok
;
272 if( ( nErr
= ValidateMasterFATs() ) != FatError::Ok
)
274 else if( ( nErr
= ValidateDirectoryEntries() ) != FatError::Ok
)
276 else if( ( nErr
= FindUnrefedChains()) != FatError::Ok
)
280 FatError
Validator::ValidateMasterFATs()
282 sal_Int32 nCount
= rIo
.m_aHdr
.GetFATSize();
285 return FatError::InMemoryError
;
287 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
289 if( ( nErr
= aFat
.Mark(rIo
.m_pFAT
->GetPage(i
, false), aFat
.GetPageSize(), -3 )) != FatError::Ok
)
292 if( rIo
.m_aHdr
.GetMasters() )
293 if( ( nErr
= aFat
.Mark(rIo
.m_aHdr
.GetFATChain( ), aFat
.GetPageSize(), -4 )) != FatError::Ok
)
299 FatError
Validator::MarkAll( StgDirEntry
*pEntry
)
302 return FatError::InMemoryError
;
304 StgIterator
aIter( *pEntry
);
305 FatError nErr
= FatError::Ok
;
306 for( StgDirEntry
* p
= aIter
.First(); p
; p
= aIter
.Next() )
308 if( p
->m_aEntry
.GetType() == STG_STORAGE
)
311 if( nErr
!= FatError::Ok
)
316 sal_Int32 nSize
= p
->m_aEntry
.GetSize();
317 if( nSize
< rIo
.m_aHdr
.GetThreshold() )
318 nErr
= aSmallFat
.Mark( p
->m_aEntry
.GetStartPage(),nSize
, -2 );
320 nErr
= aFat
.Mark( p
->m_aEntry
.GetStartPage(),nSize
, -2 );
321 if( nErr
!= FatError::Ok
)
328 FatError
Validator::ValidateDirectoryEntries()
331 return FatError::InMemoryError
;
334 FatError nErr
= MarkAll( rIo
.m_pTOC
->GetRoot() );
335 if( nErr
!= FatError::Ok
)
338 nErr
= aFat
.Mark( rIo
.m_pTOC
->GetRoot()->m_aEntry
.GetStartPage(),
339 rIo
.m_pTOC
->GetRoot()->m_aEntry
.GetSize(), -2 );
340 if( nErr
!= FatError::Ok
)
344 rIo
.m_aHdr
.GetDataFATStart(),
345 rIo
.m_aHdr
.GetDataFATSize() * aFat
.GetPageSize(), -2 );
346 if( nErr
!= FatError::Ok
)
350 rIo
.m_aHdr
.GetTOCStart(), -1, -2 );
354 FatError
Validator::FindUnrefedChains() const
356 if( aSmallFat
.HasUnrefChains() ||
357 aFat
.HasUnrefChains() )
358 return FatError::UnrefChain
;
363 FatError
StgIo::ValidateFATs()
367 std::optional
<Validator
> pV( *this );
368 bool bRet1
= !pV
->IsError(), bRet2
= true ;
371 SvFileStream
*pFileStrm
= static_cast<SvFileStream
*>( GetStrm() );
373 return FatError::InMemoryError
;
376 if( aIo
.Open( pFileStrm
->GetFileName(),
377 StreamMode::READ
| StreamMode::SHARE_DENYNONE
) &&
381 bRet2
= !pV
->IsError();
387 nErr
= bRet1
? FatError::OnFileError
: FatError::InMemoryError
;
388 else nErr
= bRet1
? FatError::Ok
: FatError::BothError
;
389 if( nErr
!= FatError::Ok
&& !m_bCopied
)
393 // DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
396 // OSL_FAIL("Do not validate (no FileStorage)");
400 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */