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