1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
\r
3 Copyright (c) 2014-2015 Datalight, Inc.
\r
4 All Rights Reserved Worldwide.
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; use version 2 of the License.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
\r
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
19 /* Businesses and individuals that for commercial or other reasons cannot
\r
20 comply with the terms of the GPLv2 license may obtain a commercial license
\r
21 before incorporating Reliance Edge into proprietary software for
\r
22 distribution in any form. Visit http://www.datalight.com/reliance-edge for
\r
26 @brief Implements core volume operations.
\r
29 #include <redcore.h>
\r
32 static bool MetarootIsValid(METAROOT *pMR, bool *pfSectorCRCIsValid);
\r
33 #ifdef REDCONF_ENDIAN_SWAP
\r
34 static void MetaRootEndianSwap(METAROOT *pMetaRoot);
\r
38 /** @brief Mount a file system volume.
\r
40 @return A negated ::REDSTATUS code indicating the operation result.
\r
42 @retval 0 Operation was successful.
\r
43 @retval -RED_EIO Volume not formatted, improperly formatted, or corrupt.
\r
45 REDSTATUS RedVolMount(void)
\r
49 #if REDCONF_READ_ONLY == 0
\r
50 ret = RedOsBDevOpen(gbRedVolNum, BDEV_O_RDWR);
\r
52 ret = RedOsBDevOpen(gbRedVolNum, BDEV_O_RDONLY);
\r
57 ret = RedVolMountMaster();
\r
61 ret = RedVolMountMetaroot();
\r
66 /* If we fail to mount, invalidate the buffers to prevent any
\r
67 confusion that could be caused by stale or corrupt metadata.
\r
69 (void)RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount);
\r
70 (void)RedOsBDevClose(gbRedVolNum);
\r
78 /** @brief Mount the master block.
\r
80 @return A negated ::REDSTATUS code indicating the operation result.
\r
82 @retval 0 Operation was successful.
\r
83 @retval -RED_EIO Master block missing, corrupt, or inconsistent with the
\r
84 compile-time driver settings.
\r
86 REDSTATUS RedVolMountMaster(void)
\r
91 /* Read the master block, to ensure that the disk was formatted with
\r
94 ret = RedBufferGet(BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR(&pMB));
\r
98 /* Verify that the driver was compiled with the same settings that
\r
99 the disk was formatted with. If not, the user has made a
\r
100 mistake: either the driver settings are wrong, or the disk needs
\r
103 if( (pMB->ulVersion != RED_DISK_LAYOUT_VERSION)
\r
104 || (pMB->ulInodeCount != gpRedVolConf->ulInodeCount)
\r
105 || (pMB->ulBlockCount != gpRedVolume->ulBlockCount)
\r
106 || (pMB->uMaxNameLen != REDCONF_NAME_MAX)
\r
107 || (pMB->uDirectPointers != REDCONF_DIRECT_POINTERS)
\r
108 || (pMB->uIndirectPointers != REDCONF_INDIRECT_POINTERS)
\r
109 || (pMB->bBlockSizeP2 != BLOCK_SIZE_P2)
\r
110 || (((pMB->bFlags & MBFLAG_API_POSIX) != 0U) != (REDCONF_API_POSIX == 1))
\r
111 || (((pMB->bFlags & MBFLAG_INODE_TIMESTAMPS) != 0U) != (REDCONF_INODE_TIMESTAMPS == 1))
\r
112 || (((pMB->bFlags & MBFLAG_INODE_BLOCKS) != 0U) != (REDCONF_INODE_BLOCKS == 1)))
\r
116 #if REDCONF_API_POSIX == 1
\r
117 else if(((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U) != (REDCONF_API_POSIX_LINK == 1))
\r
122 else if((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U)
\r
129 /* Master block configuration is valid.
\r
131 Save the sequence number of the master block in the volume,
\r
132 since we need it later (see RedVolMountMetaroot()) and we do
\r
133 not want to re-buffer the master block.
\r
135 gpRedVolume->ullSequence = pMB->hdr.ullSequence;
\r
145 /** @brief Mount the latest metaroot.
\r
147 This function also populates the volume contexts.
\r
149 @return A negated ::REDSTATUS code indicating the operation result.
\r
151 @retval 0 Operation was successful.
\r
152 @retval -RED_EIO Both metaroots are missing or corrupt.
\r
154 REDSTATUS RedVolMountMetaroot(void)
\r
158 ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[0U]);
\r
162 ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[1U]);
\r
165 /* Determine which metaroot is the most recent copy that was written
\r
170 uint8_t bMR = UINT8_MAX;
\r
171 bool fSectorCRCIsValid;
\r
173 if(MetarootIsValid(&gpRedCoreVol->aMR[0U], &fSectorCRCIsValid))
\r
177 #ifdef REDCONF_ENDIAN_SWAP
\r
178 MetaRootEndianSwap(&gpRedCoreVol->aMR[0U]);
\r
181 else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)
\r
187 /* Metaroot is not valid, so it is ignored and there's nothing
\r
194 if(MetarootIsValid(&gpRedCoreVol->aMR[1U], &fSectorCRCIsValid))
\r
196 #ifdef REDCONF_ENDIAN_SWAP
\r
197 MetaRootEndianSwap(&gpRedCoreVol->aMR[1U]);
\r
200 if((bMR != 0U) || (gpRedCoreVol->aMR[1U].hdr.ullSequence > gpRedCoreVol->aMR[0U].hdr.ullSequence))
\r
205 else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)
\r
211 /* Metaroot is not valid, so it is ignored and there's nothing
\r
219 if(bMR == UINT8_MAX)
\r
221 /* Neither metaroot was valid.
\r
227 gpRedCoreVol->bCurMR = bMR;
\r
228 gpRedMR = &gpRedCoreVol->aMR[bMR];
\r
235 /* Normally the metaroot contains the highest sequence number, but the
\r
236 master block is the last block written during format, so on a
\r
237 freshly formatted volume the master block sequence number (stored in
\r
238 gpRedVolume->ullSequence) will be higher than that in the metaroot.
\r
240 if(gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence)
\r
242 gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence;
\r
245 /* gpRedVolume->ullSequence stores the *next* sequence number; to avoid
\r
246 giving the next node written to disk the same sequence number as the
\r
247 metaroot, increment it here.
\r
249 ret = RedVolSeqNumIncrement();
\r
254 gpRedVolume->fMounted = true;
\r
255 #if REDCONF_READ_ONLY == 0
\r
256 gpRedVolume->fReadOnly = false;
\r
259 #if RESERVED_BLOCKS > 0U
\r
260 gpRedCoreVol->fUseReservedBlocks = false;
\r
262 gpRedCoreVol->ulAlmostFreeBlocks = 0U;
\r
264 gpRedCoreVol->aMR[1U - gpRedCoreVol->bCurMR] = *gpRedMR;
\r
265 gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR;
\r
266 gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];
\r
273 /** @brief Determine whether the metaroot is valid.
\r
275 @param pMR The metaroot buffer.
\r
276 @param pfSectorCRCIsValid Populated with whether the first sector of the
\r
277 metaroot buffer is valid.
\r
279 @return Whether the metaroot is valid.
\r
281 @retval true The metaroot buffer is valid.
\r
282 @retval false The metaroot buffer is invalid.
\r
284 static bool MetarootIsValid(
\r
286 bool *pfSectorCRCIsValid)
\r
290 if(pfSectorCRCIsValid == NULL)
\r
294 else if(pMR == NULL)
\r
297 *pfSectorCRCIsValid = false;
\r
299 #ifdef REDCONF_ENDIAN_SWAP
\r
300 else if(RedRev32(pMR->hdr.ulSignature) != META_SIG_METAROOT)
\r
302 else if(pMR->hdr.ulSignature != META_SIG_METAROOT)
\r
305 *pfSectorCRCIsValid = false;
\r
309 const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMR);
\r
310 uint32_t ulSectorCRC = pMR->ulSectorCRC;
\r
313 #ifdef REDCONF_ENDIAN_SWAP
\r
314 ulSectorCRC = RedRev32(ulSectorCRC);
\r
317 /* The sector CRC was zero when the CRC was computed during the
\r
318 transaction, so it must be zero here.
\r
320 pMR->ulSectorCRC = 0U;
\r
322 ulCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);
\r
324 fRet = ulCRC == ulSectorCRC;
\r
325 *pfSectorCRCIsValid = fRet;
\r
329 if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)
\r
331 ulCRC = RedCrc32Update(ulCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);
\r
334 #ifdef REDCONF_ENDIAN_SWAP
\r
335 ulCRC = RedRev32(ulCRC);
\r
338 fRet = ulCRC == pMR->hdr.ulCRC;
\r
346 #if REDCONF_READ_ONLY == 0
\r
347 /** @brief Commit a transaction point.
\r
349 @return A negated ::REDSTATUS code indicating the operation result.
\r
351 @retval 0 Operation was successful.
\r
352 @retval -RED_EIO A disk I/O error occurred.
\r
354 REDSTATUS RedVolTransact(void)
\r
358 REDASSERT(!gpRedVolume->fReadOnly); /* Should be checked by caller. */
\r
360 if(gpRedCoreVol->fBranched)
\r
362 gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks;
\r
363 gpRedCoreVol->ulAlmostFreeBlocks = 0U;
\r
365 ret = RedBufferFlush(0U, gpRedVolume->ulBlockCount);
\r
369 gpRedMR->hdr.ulSignature = META_SIG_METAROOT;
\r
370 gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence;
\r
372 ret = RedVolSeqNumIncrement();
\r
377 const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(gpRedMR);
\r
378 uint32_t ulSectorCRC;
\r
380 #ifdef REDCONF_ENDIAN_SWAP
\r
381 MetaRootEndianSwap(gpRedMR);
\r
384 gpRedMR->ulSectorCRC = 0U;
\r
386 ulSectorCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);
\r
388 if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)
\r
390 gpRedMR->hdr.ulCRC = RedCrc32Update(ulSectorCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);
\r
394 gpRedMR->hdr.ulCRC = ulSectorCRC;
\r
397 gpRedMR->ulSectorCRC = ulSectorCRC;
\r
399 #ifdef REDCONF_ENDIAN_SWAP
\r
400 gpRedMR->hdr.ulCRC = RedRev32(gpRedMR->hdr.ulCRC);
\r
401 gpRedMR->ulSectorCRC = RedRev32(gpRedMR->ulSectorCRC);
\r
404 /* Flush the block device before writing the metaroot, so that all
\r
405 previously written blocks are guaranteed to be on the media before
\r
406 the metaroot is written. Otherwise, if the block device reorders
\r
407 the writes, the metaroot could reach the media before metadata it
\r
408 points at, creating a window for disk corruption if power is lost.
\r
410 ret = RedIoFlush(gbRedVolNum);
\r
415 ret = RedIoWrite(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR);
\r
417 #ifdef REDCONF_ENDIAN_SWAP
\r
418 MetaRootEndianSwap(gpRedMR);
\r
422 /* Flush the block device to force the metaroot write to the media. This
\r
423 guarantees the transaction point is really complete before we return.
\r
427 ret = RedIoFlush(gbRedVolNum);
\r
430 /* Toggle to the other metaroot buffer. The working state and committed
\r
431 state metaroot buffers exchange places.
\r
435 uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR;
\r
437 gpRedCoreVol->aMR[bNextMR] = *gpRedMR;
\r
438 gpRedCoreVol->bCurMR = bNextMR;
\r
440 gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];
\r
442 gpRedCoreVol->fBranched = false;
\r
445 CRITICAL_ASSERT(ret == 0);
\r
453 #ifdef REDCONF_ENDIAN_SWAP
\r
454 static void MetaRootEndianSwap(
\r
455 METAROOT *pMetaRoot)
\r
457 if(pMetaRoot == NULL)
\r
463 pMetaRoot->ulSectorCRC = RedRev32(pMetaRoot->ulSectorCRC);
\r
464 pMetaRoot->ulFreeBlocks = RedRev32(pMetaRoot->ulFreeBlocks);
\r
465 #if REDCONF_API_POSIX == 1
\r
466 pMetaRoot->ulFreeInodes = RedRev32(pMetaRoot->ulFreeInodes);
\r
468 pMetaRoot->ulAllocNextBlock = RedRev32(pMetaRoot->ulAllocNextBlock);
\r
474 /** @brief Process a critical file system error.
\r
476 @param pszFileName The file in which the error occurred.
\r
477 @param ulLineNum The line number at which the error occurred.
\r
479 void RedVolCriticalError(
\r
480 const char *pszFileName,
\r
481 uint32_t ulLineNum)
\r
483 #if REDCONF_OUTPUT == 1
\r
484 #if REDCONF_READ_ONLY == 0
\r
485 if(!gpRedVolume->fReadOnly)
\r
487 RedOsOutputString("Critical file system error in Reliance Edge, setting volume to READONLY\n");
\r
492 RedOsOutputString("Critical file system error in Reliance Edge (volume already READONLY)\n");
\r
496 #if REDCONF_READ_ONLY == 0
\r
497 gpRedVolume->fReadOnly = true;
\r
500 #if REDCONF_ASSERTS == 1
\r
501 RedOsAssertFail(pszFileName, ulLineNum);
\r
509 /** @brief Increment the sequence number.
\r
511 @return A negated ::REDSTATUS code indicating the operation result.
\r
513 @retval 0 Operation was successful.
\r
514 @retval -RED_EINVAL Cannot increment sequence number: maximum value reached.
\r
515 This should not ever happen.
\r
517 REDSTATUS RedVolSeqNumIncrement(void)
\r
521 if(gpRedVolume->ullSequence == UINT64_MAX)
\r
523 /* In practice this should never, ever happen; to get here, there would
\r
524 need to be UINT64_MAX disk writes, which would take eons: longer
\r
525 than the lifetime of any product or storage media. If this assert
\r
526 fires and the current year is still written with four digits,
\r
527 suspect memory corruption.
\r
534 gpRedVolume->ullSequence++;
\r