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 #include "storpage.hxx"
21 
22 #include <sal/types.h>
23 #include <sal/log.hxx>
24 #include <rtl/string.h>
25 #include <osl/mutex.hxx>
26 
27 #include <store/types.h>
28 
29 #include "storbase.hxx"
30 #include "stordata.hxx"
31 #include "stortree.hxx"
32 
33 using namespace store;
34 
35 /*========================================================================
36  *
37  * OStorePageManager implementation.
38  *
39  *======================================================================*/
40 const sal_uInt32 OStorePageManager::m_nTypeId = sal_uInt32(0x62190120);
41 
42 /*
43  * OStorePageManager.
44  */
OStorePageManager()45 OStorePageManager::OStorePageManager()
46 {
47 }
48 
49 /*
50  * ~OStorePageManager.
51  */
~OStorePageManager()52 OStorePageManager::~OStorePageManager()
53 {
54 }
55 
56 /*
57  * isKindOf.
58  */
isKindOf(sal_uInt32 nTypeId)59 bool OStorePageManager::isKindOf (sal_uInt32 nTypeId)
60 {
61     return (nTypeId == m_nTypeId);
62 }
63 
64 /*
65  * initialize (two-phase construction).
66  * Precond: none.
67  */
initialize(ILockBytes * pLockBytes,storeAccessMode eAccessMode,sal_uInt16 & rnPageSize)68 storeError OStorePageManager::initialize (
69     ILockBytes *    pLockBytes,
70     storeAccessMode eAccessMode,
71     sal_uInt16 &    rnPageSize)
72 {
73     // Acquire exclusive access.
74     osl::MutexGuard aGuard(*this);
75 
76     // Check arguments.
77     if (!pLockBytes)
78         return store_E_InvalidParameter;
79 
80     // Initialize base.
81     storeError eErrCode = base::initialize (pLockBytes, eAccessMode, rnPageSize);
82     if (eErrCode != store_E_None)
83         return eErrCode;
84 
85     // Check for (not) writeable.
86     if (!base::isWriteable())
87     {
88         // Readonly. Load RootNode.
89         return base::loadObjectAt (m_aRoot, rnPageSize);
90     }
91 
92     // Writeable. Load or Create RootNode.
93     eErrCode = m_aRoot.loadOrCreate (rnPageSize, *this);
94     if (eErrCode == store_E_Pending)
95     {
96         // Creation notification.
97         PageHolderObject< page > xRoot (m_aRoot.get());
98 
99         // Pre-allocate left most entry (ugly, but we can't insert to left).
100         OStorePageKey aKey (rtl_crc32 (0, "/", 1), 0);
101         xRoot->insert (0, entry(aKey));
102 
103         // Save RootNode.
104         eErrCode = base::saveObjectAt (m_aRoot, rnPageSize);
105     }
106 
107     // Done.
108     return eErrCode;
109 }
110 
111 /*
112  * find_lookup (w/o split()).
113  * Internal: Precond: initialized, readable, exclusive access.
114  */
find_lookup(OStoreBTreeNodeObject & rNode,sal_uInt16 & rIndex,OStorePageKey const & rKey)115 storeError OStorePageManager::find_lookup (
116     OStoreBTreeNodeObject & rNode,
117     sal_uInt16 &            rIndex,
118     OStorePageKey const &   rKey)
119 {
120     // Find Node and Index.
121     storeError eErrCode = m_aRoot.find_lookup (rNode, rIndex, rKey, *this);
122     if (eErrCode != store_E_None)
123         return eErrCode;
124 
125     // Greater or Equal.
126     PageHolderObject< page > xPage (rNode.get());
127     SAL_WARN_IF(rIndex >= xPage->usageCount(), "store", "store::PageManager::find_lookup(): logic error");
128     entry e (xPage->m_pData[rIndex]);
129 
130     // Check for exact match.
131     if (e.compare(entry(rKey)) != entry::COMPARE_EQUAL)
132     {
133         // Page not present.
134         return store_E_NotExists;
135     }
136 
137     // Check address.
138     if (e.m_aLink.location() == STORE_PAGE_NULL)
139     {
140         // Page not present.
141         return store_E_NotExists;
142     }
143 
144     return store_E_None;
145 }
146 
147 /*
148  * remove_Impl (possibly down from root).
149  * Internal: Precond: initialized, writeable, exclusive access.
150  */
151 
remove_Impl(entry & rEntry)152 storeError OStorePageManager::remove_Impl (entry & rEntry)
153 {
154     OStoreBTreeNodeObject aNode (m_aRoot.get());
155 
156     // Check current page index.
157     PageHolderObject< page > xPage (aNode.get());
158     sal_uInt16 i = xPage->find (rEntry), n = xPage->usageCount();
159     if (i >= n)
160     {
161         // Path to entry not exists (Must not happen(?)).
162         return store_E_NotExists;
163     }
164 
165     // Compare entry.
166     entry::CompareResult result = rEntry.compare (xPage->m_pData[i]);
167 
168     // Iterate down until equal match.
169     while ((result == entry::COMPARE_GREATER) && (xPage->depth() > 0))
170     {
171         // Check link address.
172         sal_uInt32 const nAddr = xPage->m_pData[i].m_aLink.location();
173         if (nAddr == STORE_PAGE_NULL)
174         {
175             // Path to entry not exists (Must not happen(?)).
176             return store_E_NotExists;
177         }
178 
179         // Load link page.
180         storeError eErrCode = loadObjectAt (aNode, nAddr);
181         if (eErrCode != store_E_None)
182             return eErrCode;
183 
184         PageHolderObject< page > xNext (aNode.get());
185         xNext.swap (xPage);
186 
187         // Check index.
188         i = xPage->find (rEntry);
189         n = xPage->usageCount();
190         if (i >= n)
191         {
192             // Path to entry not exists (Must not happen(?)).
193             return store_E_NotExists;
194         }
195 
196         // Compare entry.
197         result = rEntry.compare (xPage->m_pData[i]);
198     }
199 
200     SAL_WARN_IF(
201         result == entry::COMPARE_LESS,
202         "store",
203         "OStorePageManager::remove(): find failed");
204 
205     // Check entry comparison.
206     if (result == entry::COMPARE_LESS)
207     {
208         // Must not happen.
209         return store_E_Unknown;
210     }
211 
212     // Remove down from current page (recursive).
213     return aNode.remove (i, rEntry, *this);
214 }
215 
216 /*
217  * namei.
218  * Precond: none (static).
219  */
namei(const rtl_String * pPath,const rtl_String * pName,OStorePageKey & rKey)220 storeError OStorePageManager::namei (
221     const rtl_String *pPath, const rtl_String *pName, OStorePageKey &rKey)
222 {
223     // Check parameter.
224     if (!(pPath && pName))
225         return store_E_InvalidParameter;
226 
227     // Check name length.
228     if (pName->length >= STORE_MAXIMUM_NAMESIZE)
229         return store_E_NameTooLong;
230 
231     // Transform pathname into key.
232     rKey.m_nLow  = store::htonl(rtl_crc32 (0, pName->buffer, pName->length));
233     rKey.m_nHigh = store::htonl(rtl_crc32 (0, pPath->buffer, pPath->length));
234 
235     // Done.
236     return store_E_None;
237 }
238 
239 /*
240  * iget.
241  * Precond: initialized.
242  */
iget(OStoreDirectoryPageObject & rPage,sal_uInt32 nAttrib,const rtl_String * pPath,const rtl_String * pName,storeAccessMode eMode)243 storeError OStorePageManager::iget (
244     OStoreDirectoryPageObject & rPage,
245     sal_uInt32                  nAttrib,
246     const rtl_String          * pPath,
247     const rtl_String          * pName,
248     storeAccessMode             eMode)
249 {
250     // Acquire exclusive access.
251     osl::MutexGuard aGuard(*this);
252 
253     // Check precond.
254     if (!self::isValid())
255         return store_E_InvalidAccess;
256 
257     // Setup inode page key.
258     OStorePageKey aKey;
259     storeError eErrCode = namei (pPath, pName, aKey);
260     if (eErrCode != store_E_None)
261         return eErrCode;
262 
263     // Check for directory.
264     if (nAttrib & STORE_ATTRIB_ISDIR)
265     {
266         // Ugly, but necessary (backward compatibility).
267         aKey.m_nLow = store::htonl(rtl_crc32 (store::ntohl(aKey.m_nLow), "/", 1));
268     }
269 
270     // Load inode page.
271     eErrCode = load_dirpage_Impl (aKey, rPage);
272     if (eErrCode != store_E_None)
273     {
274         // Check mode and reason.
275         if (eErrCode != store_E_NotExists)
276             return eErrCode;
277 
278         if (eMode == storeAccessMode::ReadWrite)
279             return store_E_NotExists;
280         if (eMode == storeAccessMode::ReadOnly)
281             return store_E_NotExists;
282 
283         if (!base::isWriteable())
284             return store_E_AccessViolation;
285 
286         // Create inode page.
287         eErrCode = rPage.construct< inode >(base::allocator());
288         if (eErrCode != store_E_None)
289             return eErrCode;
290 
291         // Setup inode nameblock.
292         PageHolderObject< inode > xPage (rPage.get());
293 
294         rPage.key (aKey);
295         rPage.attrib (nAttrib);
296 
297         memcpy (
298             &(xPage->m_aNameBlock.m_pData[0]),
299             pName->buffer, pName->length);
300 
301         // Save inode page.
302         eErrCode = save_dirpage_Impl (aKey, rPage);
303         if (eErrCode != store_E_None)
304             return eErrCode;
305     }
306 
307     // Check for symbolic link.
308     if (rPage.attrib() & STORE_ATTRIB_ISLINK)
309     {
310         // Obtain 'Destination' page key.
311         PageHolderObject< inode > xPage (rPage.get());
312         OStorePageKey aDstKey;
313         memcpy (&aDstKey, &(xPage->m_pData[0]), sizeof(aDstKey));
314 
315         // Load 'Destination' inode.
316         eErrCode = load_dirpage_Impl (aDstKey, rPage);
317         if (eErrCode != store_E_None)
318             return eErrCode;
319     }
320 
321     // Done.
322     return store_E_None;
323 }
324 
325 /*
326  * iterate.
327  * Precond: initialized.
328  * ToDo: skip hardlink entries.
329  */
iterate(OStorePageKey & rKey,OStorePageLink & rLink,sal_uInt32 & rAttrib)330 storeError OStorePageManager::iterate (
331     OStorePageKey &  rKey,
332     OStorePageLink & rLink,
333     sal_uInt32 &     rAttrib)
334 {
335     // Acquire exclusive access.
336     osl::MutexGuard aGuard(*this);
337 
338     // Check precond.
339     if (!self::isValid())
340         return store_E_InvalidAccess;
341 
342     // Find NodePage and Index.
343     OStoreBTreeNodeObject aNode;
344     sal_uInt16 i = 0;
345     storeError eErrCode = m_aRoot.find_lookup (aNode, i, rKey, *this);
346     if (eErrCode != store_E_None)
347         return eErrCode;
348 
349     // GreaterEqual. Found next entry.
350     PageHolderObject< page > xNode (aNode.get());
351     entry e (xNode->m_pData[i]);
352 
353     // Setup result.
354     rKey    = e.m_aKey;
355     rLink   = e.m_aLink;
356     rAttrib = store::ntohl(e.m_nAttrib);
357 
358     // Done.
359     return store_E_None;
360 }
361 
362 /*
363  * load => private: iget() @@@
364  * Internal: Precond: initialized, exclusive access.
365  */
load_dirpage_Impl(const OStorePageKey & rKey,OStoreDirectoryPageObject & rPage)366 storeError OStorePageManager::load_dirpage_Impl (
367     const OStorePageKey       &rKey,
368     OStoreDirectoryPageObject &rPage)
369 {
370     // Find Node and Index.
371     OStoreBTreeNodeObject aNode;
372     sal_uInt16 i = 0;
373     storeError eErrCode = find_lookup (aNode, i, rKey);
374     if (eErrCode != store_E_None)
375         return eErrCode;
376 
377     // Existing entry. Load page.
378     PageHolderObject< page > xNode (aNode.get());
379     entry e (xNode->m_pData[i]);
380     return loadObjectAt (rPage, e.m_aLink.location());
381 }
382 
383 /*
384  * save => private: iget(), rebuild() @@@
385  * Internal: Precond: initialized, writeable, exclusive access.
386  */
save_dirpage_Impl(const OStorePageKey & rKey,OStoreDirectoryPageObject & rPage)387 storeError OStorePageManager::save_dirpage_Impl (
388     const OStorePageKey       &rKey,
389     OStoreDirectoryPageObject &rPage)
390 {
391     // Find NodePage and Index.
392     node aNode;
393     sal_uInt16 i = 0;
394 
395     storeError eErrCode = m_aRoot.find_insert (aNode, i, rKey, *this);
396     PageHolderObject< page > xNode (aNode.get());
397     if (eErrCode != store_E_None)
398     {
399         if (eErrCode != store_E_AlreadyExists)
400             return eErrCode;
401 
402         // Existing entry.
403         entry e (xNode->m_pData[i]);
404         if (e.m_aLink.location() != STORE_PAGE_NULL)
405         {
406             // Save page to existing location.
407             return saveObjectAt (rPage, e.m_aLink.location());
408         }
409 
410         // Allocate page.
411         eErrCode = base::allocate (rPage);
412         if (eErrCode != store_E_None)
413             return eErrCode;
414 
415         // Update page location.
416         xNode->m_pData[i].m_aLink = rPage.location();
417 
418         // Save modified NodePage.
419         return saveObjectAt (aNode, aNode.location());
420     }
421 
422     // Allocate page.
423     eErrCode = base::allocate (rPage);
424     if (eErrCode != store_E_None)
425         return eErrCode;
426 
427     // Insert.
428     OStorePageLink aLink (rPage.location());
429     xNode->insert (i + 1, entry (rKey, aLink));
430 
431     // Save modified NodePage.
432     return saveObjectAt (aNode, aNode.location());
433 }
434 
435 /*
436  * remove.
437  * Precond: initialized, writeable.
438  */
remove(const OStorePageKey & rKey)439 storeError OStorePageManager::remove (const OStorePageKey &rKey)
440 {
441     // Acquire exclusive access.
442     osl::MutexGuard aGuard(*this);
443 
444     // Check precond.
445     if (!self::isValid())
446         return store_E_InvalidAccess;
447 
448     if (!base::isWriteable())
449         return store_E_AccessViolation;
450 
451     // Find NodePage and index.
452     OStoreBTreeNodeObject aNodePage;
453     sal_uInt16 i = 0;
454     storeError eErrCode = find_lookup (aNodePage, i, rKey);
455     if (eErrCode != store_E_None)
456         return eErrCode;
457 
458     // Existing entry.
459     PageHolderObject< page > xNodePage (aNodePage.get());
460     entry e (xNodePage->m_pData[i]);
461 
462     // Check for (not a) hardlink.
463     if (!(store::ntohl(e.m_nAttrib) & STORE_ATTRIB_ISLINK))
464     {
465         // Load directory page.
466         OStoreDirectoryPageObject aPage;
467         eErrCode = base::loadObjectAt (aPage, e.m_aLink.location());
468         if (eErrCode != store_E_None)
469             return eErrCode;
470 
471         inode_holder_type xNode (aPage.get());
472 
473         // Acquire page write access.
474         OStorePageDescriptor aDescr (xNode->m_aDescr);
475         eErrCode = base::acquirePage (aDescr, storeAccessMode::ReadWrite);
476         if (eErrCode != store_E_None)
477             return eErrCode;
478 
479         // Check for symbolic link.
480         if (!(aPage.attrib() & STORE_ATTRIB_ISLINK))
481         {
482             // Ordinary inode. Determine 'Data' scope.
483             inode::ChunkScope eScope = xNode->scope (aPage.dataLength());
484             if (eScope == inode::SCOPE_EXTERNAL)
485             {
486                 // External 'Data' scope. Truncate all external data pages.
487                 eErrCode = aPage.truncate (0, *this);
488                 if (eErrCode != store_E_None)
489                     return eErrCode;
490             }
491 
492             // Truncate internal data page.
493             memset (&(xNode->m_pData[0]), 0, xNode->capacity());
494             aPage.dataLength (0);
495         }
496 
497         // Release page write access.
498         base::releasePage (aDescr);
499 
500         // Release and free directory page.
501         (void)base::free (aPage.location());
502     }
503 
504     // Remove entry.
505     return remove_Impl (e);
506 }
507 
508 
509 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
510