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 .
20 #include <sal/config.h>
22 #include "storbios.hxx"
24 #include <sal/types.h>
25 #include <sal/log.hxx>
27 #include <rtl/alloc.h>
28 #include <rtl/ref.hxx>
30 #include <osl/diagnose.h>
31 #include <osl/mutex.hxx>
33 #include <store/types.h>
34 #include "lockbyte.hxx"
35 #include "storcach.hxx"
37 using namespace store
;
39 constexpr sal_uInt32 STORE_MAGIC_SUPERBLOCK
= 0x484D5343;
43 struct OStoreSuperBlock
45 typedef OStorePageGuard G
;
46 typedef OStorePageDescriptor D
;
47 typedef OStorePageLink L
;
56 static const size_t theSize
= sizeof(G
) + sizeof(D
) + 2 * (sizeof(L
) + sizeof(sal_uInt32
));
58 explicit OStoreSuperBlock (sal_uInt16 nPageSize
)
59 : m_aGuard (STORE_MAGIC_SUPERBLOCK
),
60 m_aDescr (nPageSize
, nPageSize
, STORE_MINIMUM_PAGESIZE
),
61 m_nMarked (store::htonl(0)),
63 m_nUnused (store::htonl(0)),
67 bool operator== (const OStoreSuperBlock
& rhs
) const
69 return ((m_aGuard
== rhs
.m_aGuard
) &&
70 (m_aDescr
== rhs
.m_aDescr
) &&
71 (m_nMarked
== rhs
.m_nMarked
) &&
72 (m_aMarked
== rhs
.m_aMarked
) &&
73 (m_nUnused
== rhs
.m_nUnused
) &&
74 (m_aUnused
== rhs
.m_aUnused
) );
77 sal_uInt32
unusedCount() const
79 return store::ntohl(m_nUnused
);
82 const L
& unusedHead() const
87 void unusedInsert (const L
& rLink
)
89 sal_uInt32 nUnused
= unusedCount();
90 m_nUnused
= store::htonl(nUnused
+ 1);
94 void unusedRemove (const L
& rLink
)
96 sal_uInt32 nUnused
= unusedCount();
97 m_nUnused
= store::htonl(nUnused
- 1);
103 m_nUnused
= store::htonl(0);
109 sal_uInt32 nCRC32
= rtl_crc32 (0, &m_aGuard
.m_nMagic
, sizeof(sal_uInt32
));
110 nCRC32
= rtl_crc32 (nCRC32
, &m_aDescr
, static_cast<sal_uInt32
>(theSize
- sizeof(G
)));
111 m_aGuard
.m_nCRC32
= store::htonl(nCRC32
);
114 storeError
verify() const
116 sal_uInt32 nMagic
= store::ntohl(m_aGuard
.m_nMagic
);
117 if (nMagic
!= STORE_MAGIC_SUPERBLOCK
)
118 return store_E_WrongFormat
;
120 sal_uInt32 nCRC32
= rtl_crc32 (0, &m_aGuard
.m_nMagic
, sizeof(sal_uInt32
));
121 nCRC32
= rtl_crc32 (nCRC32
, &m_aDescr
, static_cast<sal_uInt32
>(theSize
- sizeof(G
)));
122 if (m_aGuard
.m_nCRC32
!= store::htonl(nCRC32
))
123 return store_E_InvalidChecksum
;
134 struct SuperBlockPage
136 typedef OStoreSuperBlock SuperBlock
;
138 SuperBlock m_aSuperOne
;
139 SuperBlock m_aSuperTwo
;
141 static const size_t theSize
= 2 * SuperBlock::theSize
;
142 static const sal_uInt16 thePageSize
= theSize
;
143 static_assert(STORE_MINIMUM_PAGESIZE
>= thePageSize
, "must be at least thePageSize");
145 static void * operator new (size_t n
)
147 return std::malloc(sal::static_int_cast
<sal_Size
>(n
));
150 static void operator delete (void * p
)
155 static void * operator new (SAL_UNUSED_PARAMETER
size_t, sal_uInt16 nPageSize
)
157 return rtl_allocateZeroMemory (sal::static_int_cast
<sal_Size
>(nPageSize
));
160 static void operator delete (void * p
, SAL_UNUSED_PARAMETER sal_uInt16
)
165 explicit SuperBlockPage (sal_uInt16 nPageSize
= thePageSize
)
166 : m_aSuperOne(nPageSize
),
167 m_aSuperTwo(nPageSize
)
170 storeError
save (OStorePageBIOS
const & rBIOS
, sal_uInt32 nSize
= theSize
)
173 m_aSuperTwo
= m_aSuperOne
;
174 return rBIOS
.write (0, this, nSize
);
179 storeError
unusedHead (
180 OStorePageBIOS
const & rBIOS
,
181 PageData
& rPageHead
);
183 storeError
unusedPop (
184 OStorePageBIOS
const & rBIOS
,
185 PageData
const & rPageHead
);
187 storeError
unusedPush (
188 OStorePageBIOS
const & rBIOS
,
191 storeError
verify (OStorePageBIOS
const & rBIOS
);
197 Get freelist head (alloc page, step 1).
199 storeError
SuperBlockPage::unusedHead (OStorePageBIOS
const & rBIOS
, PageData
& rPageHead
)
201 storeError eErrCode
= verify (rBIOS
);
202 if (eErrCode
!= store_E_None
)
205 // Check freelist head.
206 OStorePageLink
const aListHead (m_aSuperOne
.unusedHead());
207 if (aListHead
.location() == 0)
209 // Freelist empty, see SuperBlock::ctor().
210 rPageHead
.location (STORE_PAGE_NULL
);
215 eErrCode
= rBIOS
.read (aListHead
.location(), &rPageHead
, PageData::theSize
);
216 if (eErrCode
!= store_E_None
)
219 eErrCode
= rPageHead
.verify (aListHead
.location());
220 if (eErrCode
!= store_E_None
)
223 // Verify page is unused.
224 sal_uInt32
const nAddr
= rPageHead
.m_aUnused
.location();
225 if (nAddr
== STORE_PAGE_NULL
)
227 SAL_WARN("store", "store::SuperBlock::unusedHead(): page not free");
230 rPageHead
.location (STORE_PAGE_NULL
);
232 // Recovery: Reset freelist to empty.
233 m_aSuperOne
.unusedReset();
234 eErrCode
= save (rBIOS
);
240 Pop freelist head (alloc page, step 2).
242 storeError
SuperBlockPage::unusedPop (OStorePageBIOS
const & rBIOS
, PageData
const & rPageHead
)
244 sal_uInt32
const nAddr
= rPageHead
.m_aUnused
.location();
245 OSL_PRECOND(nAddr
!= STORE_PAGE_NULL
, "store::SuperBlock::unusedPop(): page not free");
246 if (nAddr
== STORE_PAGE_NULL
)
247 return store_E_CantSeek
;
249 // Pop from FreeList.
250 OStorePageLink
const aListHead (nAddr
);
251 m_aSuperOne
.unusedRemove (aListHead
);
256 Push new freelist head.
258 storeError
SuperBlockPage::unusedPush (OStorePageBIOS
const & rBIOS
, sal_uInt32 nAddr
)
260 storeError eErrCode
= verify (rBIOS
);
261 if (eErrCode
!= store_E_None
)
265 eErrCode
= rBIOS
.read (nAddr
, &aPageHead
, PageData::theSize
);
266 if (eErrCode
!= store_E_None
)
269 eErrCode
= aPageHead
.verify (nAddr
);
270 if (eErrCode
!= store_E_None
)
273 aPageHead
.m_aUnused
= m_aSuperOne
.unusedHead();
274 aPageHead
.guard (nAddr
);
276 eErrCode
= rBIOS
.write (nAddr
, &aPageHead
, PageData::theSize
);
277 if (eErrCode
!= store_E_None
)
280 OStorePageLink
const aListHead (nAddr
);
281 m_aSuperOne
.unusedInsert(aListHead
);
286 Verify (with repair).
288 storeError
SuperBlockPage::verify (OStorePageBIOS
const & rBIOS
)
291 storeError eErrCode
= m_aSuperOne
.verify();
292 if (eErrCode
== store_E_None
)
294 // Ok. Verify 2nd copy.
295 eErrCode
= m_aSuperTwo
.verify();
296 if (eErrCode
== store_E_None
)
298 // Ok. Ensure identical copies (1st copy wins).
299 if (!(m_aSuperOne
== m_aSuperTwo
))
301 // Different. Replace 2nd copy with 1st copy.
302 m_aSuperTwo
= m_aSuperOne
;
305 if (rBIOS
.isWriteable())
306 eErrCode
= rBIOS
.write (0, this, theSize
);
308 eErrCode
= store_E_None
;
313 // Failure. Replace 2nd copy with 1st copy.
314 m_aSuperTwo
= m_aSuperOne
;
317 if (rBIOS
.isWriteable())
318 eErrCode
= rBIOS
.write (0, this, theSize
);
320 eErrCode
= store_E_None
;
325 // Failure. Verify 2nd copy.
326 eErrCode
= m_aSuperTwo
.verify();
327 if (eErrCode
== store_E_None
)
329 // Ok. Replace 1st copy with 2nd copy.
330 m_aSuperOne
= m_aSuperTwo
;
333 if (rBIOS
.isWriteable())
334 eErrCode
= rBIOS
.write (0, this, theSize
);
336 eErrCode
= store_E_None
;
341 SAL_WARN("store", "OStoreSuperBlockPage::verify(): double failure.");
349 OStorePageBIOS::Ace::Ace()
350 : m_next (this), m_prev (this), m_addr (STORE_PAGE_NULL
), m_used (0)
353 OStorePageBIOS::Ace::~Ace()
355 m_next
->m_prev
= m_prev
;
356 m_prev
->m_next
= m_next
;
360 SAL_CALL
OStorePageBIOS::Ace::constructor (
361 void * obj
, SAL_UNUSED_PARAMETER
void*)
363 Ace
* ace
= static_cast<Ace
*>(obj
);
364 ace
->m_next
= ace
->m_prev
= ace
;
368 OStorePageBIOS::Ace
*
369 OStorePageBIOS::Ace::find (OStorePageBIOS::Ace
* head
, sal_uInt32 addr
)
371 OStorePageBIOS::Ace
* entry
;
372 for (entry
= head
->m_next
; entry
!= head
; entry
= entry
->m_next
)
374 if (entry
->m_addr
>= addr
)
381 OStorePageBIOS::Ace::insert (OStorePageBIOS::Ace
* head
, OStorePageBIOS::Ace
* entry
)
383 // insert entry at queue tail (before head).
384 entry
->m_next
= head
;
385 entry
->m_prev
= head
->m_prev
;
386 head
->m_prev
= entry
;
387 entry
->m_prev
->m_next
= entry
;
393 class OStorePageBIOS::AceCache
395 rtl_cache_type
* m_ace_cache
;
398 static AceCache
& get();
400 OStorePageBIOS::Ace
*
401 create (sal_uInt32 addr
);
404 destroy (OStorePageBIOS::Ace
* ace
);
413 OStorePageBIOS::AceCache
&
414 OStorePageBIOS::AceCache::get()
416 static AceCache g_ace_cache
;
420 OStorePageBIOS::AceCache::AceCache()
422 m_ace_cache
= rtl_cache_create (
424 sizeof (OStorePageBIOS::Ace
),
426 OStorePageBIOS::Ace::constructor
,
427 nullptr, // destructor,
430 nullptr, // default source,
435 OStorePageBIOS::AceCache::~AceCache()
437 rtl_cache_destroy (m_ace_cache
);
438 m_ace_cache
= nullptr;
441 OStorePageBIOS::Ace
*
442 OStorePageBIOS::AceCache::create (sal_uInt32 addr
)
444 Ace
* ace
= static_cast<Ace
*>(rtl_cache_alloc (m_ace_cache
));
447 // verify invariant state.
448 OSL_ASSERT((ace
->m_next
== ace
) && (ace
->m_prev
== ace
));
458 OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace
* ace
)
462 // remove from queue (if any).
463 ace
->m_next
->m_prev
= ace
->m_prev
;
464 ace
->m_prev
->m_next
= ace
->m_next
;
466 // restore invariant state.
467 ace
->m_next
= ace
->m_prev
= ace
;
470 rtl_cache_free (m_ace_cache
, ace
);
474 OStorePageBIOS::OStorePageBIOS()
475 : m_bWriteable (false)
479 OStorePageBIOS::~OStorePageBIOS()
484 storeError
OStorePageBIOS::initialize (
485 ILockBytes
* pLockBytes
,
486 storeAccessMode eAccessMode
,
487 sal_uInt16
& rnPageSize
)
489 // Acquire exclusive access.
490 osl::MutexGuard
aGuard (m_aMutex
);
493 storeError eErrCode
= initialize_Impl (pLockBytes
, eAccessMode
, rnPageSize
);
494 if (eErrCode
!= store_E_None
)
504 @pre exclusive access
506 storeError
OStorePageBIOS::initialize_Impl (
507 ILockBytes
* pLockBytes
,
508 storeAccessMode eAccessMode
,
509 sal_uInt16
& rnPageSize
)
515 m_xLockBytes
= pLockBytes
;
516 if (!m_xLockBytes
.is())
517 return store_E_InvalidParameter
;
518 m_bWriteable
= (eAccessMode
!= storeAccessMode::ReadOnly
);
520 // Check access mode.
521 storeError eErrCode
= store_E_None
;
522 if (eAccessMode
!= storeAccessMode::Create
)
524 // Load SuperBlock page.
525 m_pSuper
.reset(new SuperBlockPage());
527 eErrCode
= read (0, m_pSuper
.get(), SuperBlockPage::theSize
);
528 if (eErrCode
== store_E_None
)
530 // Verify SuperBlock page (with repair).
531 eErrCode
= m_pSuper
->verify (*this);
536 // Truncate to zero length.
537 eErrCode
= m_xLockBytes
->setSize(0);
538 if (eErrCode
!= store_E_None
)
541 // Mark as not existing.
542 eErrCode
= store_E_NotExists
;
545 if (eErrCode
!= store_E_None
)
548 if (eErrCode
!= store_E_NotExists
)
552 if (eAccessMode
== storeAccessMode::ReadOnly
)
553 return store_E_NotExists
;
554 if (eAccessMode
== storeAccessMode::ReadWrite
)
555 return store_E_NotExists
;
558 if ((STORE_MINIMUM_PAGESIZE
> rnPageSize
) || (rnPageSize
> STORE_MAXIMUM_PAGESIZE
))
559 return store_E_InvalidParameter
;
560 rnPageSize
= ((rnPageSize
+ STORE_MINIMUM_PAGESIZE
- 1) & ~(STORE_MINIMUM_PAGESIZE
- 1));
562 // Create initial page (w/ SuperBlock).
563 m_pSuper
.reset(new(rnPageSize
) SuperBlockPage(rnPageSize
));
564 eErrCode
= m_pSuper
->save (*this, rnPageSize
);
566 if (eErrCode
== store_E_None
)
569 rnPageSize
= store::ntohs(m_pSuper
->m_aSuperOne
.m_aDescr
.m_nSize
);
571 // Create page allocator.
572 eErrCode
= m_xLockBytes
->initialize (m_xAllocator
, rnPageSize
);
573 if (eErrCode
!= store_E_None
)
576 // Create page cache.
577 eErrCode
= PageCache_createInstance (m_xCache
, rnPageSize
);
583 @pre exclusive access.
585 void OStorePageBIOS::cleanup_Impl()
587 // Check referer count.
588 if (m_ace_head
.m_used
> 0)
590 // Report remaining referer count.
591 SAL_INFO("store", "referer count: " << m_ace_head
.m_used
);
592 for (Ace
* ace
= m_ace_head
.m_next
; ace
!= &m_ace_head
; ace
= m_ace_head
.m_next
)
594 m_ace_head
.m_used
-= ace
->m_used
;
595 AceCache::get().destroy (ace
);
597 OSL_ENSURE(m_ace_head
.m_used
== 0, "store::PageBIOS::cleanup_Impl(): logic error");
600 // Release SuperBlock page.
603 // Release PageCache.
606 // Release PageAllocator.
607 m_xAllocator
.clear();
609 // Release LockBytes.
610 m_xLockBytes
.clear();
614 @pre initialized, exclusive access.
616 storeError
OStorePageBIOS::read (
617 sal_uInt32 nAddr
, void *pData
, sal_uInt32 nSize
) const
620 if (!m_xLockBytes
.is())
621 return store_E_InvalidAccess
;
624 return m_xLockBytes
->readAt (nAddr
, pData
, nSize
);
628 @pre initialized, writeable, exclusive access.
630 storeError
OStorePageBIOS::write (
631 sal_uInt32 nAddr
, const void *pData
, sal_uInt32 nSize
) const
634 if (!m_xLockBytes
.is())
635 return store_E_InvalidAccess
;
637 return store_E_AccessViolation
;
640 return m_xLockBytes
->writeAt (nAddr
, pData
, nSize
);
646 storeError
OStorePageBIOS::acquirePage (
647 const OStorePageDescriptor
& rDescr
, storeAccessMode eMode
)
649 // Acquire exclusive access.
650 osl::MutexGuard
aGuard (m_aMutex
);
653 if (!m_xLockBytes
.is())
654 return store_E_InvalidAccess
;
656 // Check access mode.
657 if (!(m_bWriteable
|| (eMode
== storeAccessMode::ReadOnly
)))
658 return store_E_AccessViolation
;
660 // Find access control list entry.
661 Ace
* ace
= Ace::find (&m_ace_head
, rDescr
.m_nAddr
);
662 if (ace
->m_addr
== rDescr
.m_nAddr
)
664 // Acquire existing entry (with ShareDenyWrite).
665 if (eMode
== storeAccessMode::ReadOnly
)
668 return store_E_AccessViolation
;
673 Ace
* entry
= AceCache::get().create (rDescr
.m_nAddr
);
675 return store_E_OutOfMemory
;
676 Ace::insert (ace
, entry
);
679 // Increment total referer count and finish.
680 m_ace_head
.m_used
+= 1;
687 storeError
OStorePageBIOS::releasePage (const OStorePageDescriptor
& rDescr
)
689 // Acquire exclusive access.
690 osl::MutexGuard
aGuard (m_aMutex
);
693 if (!m_xLockBytes
.is())
694 return store_E_InvalidAccess
;
696 // Find access control list entry.
697 Ace
* ace
= Ace::find (&m_ace_head
, rDescr
.m_nAddr
);
698 if (ace
->m_addr
!= rDescr
.m_nAddr
)
699 return store_E_NotExists
;
701 // Release existing entry.
705 AceCache::get().destroy (ace
);
707 // Decrement total referer count and finish.
708 m_ace_head
.m_used
-= 1;
713 @pre initialized, writeable.
715 storeError
OStorePageBIOS::allocate (
716 OStorePageObject
& rPage
)
718 // Acquire exclusive access.
719 osl::MutexGuard
aGuard (m_aMutex
);
722 if (!m_xLockBytes
.is())
723 return store_E_InvalidAccess
;
725 return store_E_AccessViolation
;
727 // Check allocation type.
728 storeError eErrCode
= store_E_None
;
729 // Try freelist head.
731 eErrCode
= m_pSuper
->unusedHead (*this, aPageHead
);
732 if (eErrCode
!= store_E_None
)
735 sal_uInt32
const nAddr
= aPageHead
.location();
736 if (nAddr
!= STORE_PAGE_NULL
)
739 eErrCode
= saveObjectAt_Impl (rPage
, nAddr
);
740 if (eErrCode
!= store_E_None
)
743 // Pop freelist head and finish.
744 return m_pSuper
->unusedPop (*this, aPageHead
);
747 // Allocate from EOF. Determine current size.
748 sal_uInt32 nSize
= STORE_PAGE_NULL
;
749 eErrCode
= m_xLockBytes
->getSize (nSize
);
750 if (eErrCode
!= store_E_None
)
753 // Save page at current EOF.
754 return saveObjectAt_Impl (rPage
, nSize
);
758 @pre initialized, writeable.
760 storeError
OStorePageBIOS::free (sal_uInt32 nAddr
)
762 // Acquire exclusive access.
763 osl::MutexGuard
aGuard (m_aMutex
);
766 if (!m_xLockBytes
.is())
767 return store_E_InvalidAccess
;
769 return store_E_AccessViolation
;
772 (void) m_xCache
->removePageAt (nAddr
);
774 // Push onto freelist.
775 return m_pSuper
->unusedPush (*this, nAddr
);
779 @pre initialized, readable.
781 storeError
OStorePageBIOS::loadObjectAt (OStorePageObject
& rPage
, sal_uInt32 nAddr
)
783 // Acquire exclusive access.
784 osl::MutexGuard
aGuard (m_aMutex
);
787 if (!m_xLockBytes
.is())
788 return store_E_InvalidAccess
;
790 return loadObjectAt_Impl (rPage
, nAddr
);
794 @pre initialized, readable, exclusive access.
796 storeError
OStorePageBIOS::loadObjectAt_Impl (OStorePageObject
& rPage
, sal_uInt32 nAddr
) const
798 storeError eErrCode
= m_xCache
->lookupPageAt (rPage
.get(), nAddr
);
799 if (eErrCode
!= store_E_NotExists
)
803 eErrCode
= m_xLockBytes
->readPageAt (rPage
.get(), nAddr
);
804 if (eErrCode
!= store_E_None
)
808 eErrCode
= rPage
.verify (nAddr
);
809 if (eErrCode
!= store_E_None
)
812 // Mark page as clean.
816 return m_xCache
->insertPageAt (rPage
.get(), nAddr
);
820 @pre initialized, writeable.
822 storeError
OStorePageBIOS::saveObjectAt (OStorePageObject
& rPage
, sal_uInt32 nAddr
)
824 // Acquire exclusive access.
825 osl::MutexGuard
aGuard (m_aMutex
);
828 if (!m_xLockBytes
.is())
829 return store_E_InvalidAccess
;
831 return store_E_AccessViolation
;
834 return saveObjectAt_Impl (rPage
, nAddr
);
838 @pre initialized, writeable, exclusive access.
840 storeError
OStorePageBIOS::saveObjectAt_Impl (OStorePageObject
& rPage
, sal_uInt32 nAddr
) const
842 // Guard page (incl. set location).
843 storeError eErrCode
= rPage
.guard (nAddr
);
844 if (eErrCode
!= store_E_None
)
848 eErrCode
= m_xLockBytes
->writePageAt(rPage
.get(), nAddr
);
849 if (eErrCode
!= store_E_None
)
852 // Mark page as clean.
856 return m_xCache
->updatePageAt (rPage
.get(), nAddr
);
862 storeError
OStorePageBIOS::close()
864 // Acquire exclusive access.
865 osl::MutexGuard
aGuard (m_aMutex
);
877 storeError
OStorePageBIOS::flush()
879 // Acquire exclusive access.
880 osl::MutexGuard
aGuard (m_aMutex
);
883 if (!m_xLockBytes
.is())
884 return store_E_InvalidAccess
;
886 // Flush LockBytes and finish.
887 return m_xLockBytes
->flush();
890 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */