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 (void)RedOsBDevClose(gbRedVolNum);
\r
74 /** @brief Mount the master block.
\r
76 @return A negated ::REDSTATUS code indicating the operation result.
\r
78 @retval 0 Operation was successful.
\r
79 @retval -RED_EIO Master block missing, corrupt, or inconsistent with the
\r
80 compile-time driver settings.
\r
82 REDSTATUS RedVolMountMaster(void)
\r
87 /* Read the master block, to ensure that the disk was formatted with
\r
90 ret = RedBufferGet(BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR(&pMB));
\r
94 /* Verify that the driver was compiled with the same settings that
\r
95 the disk was formatted with. If not, the user has made a
\r
96 mistake: either the driver settings are wrong, or the disk needs
\r
99 if( (pMB->ulVersion != RED_DISK_LAYOUT_VERSION)
\r
100 || (pMB->ulInodeCount != gpRedVolConf->ulInodeCount)
\r
101 || (pMB->ulBlockCount != gpRedVolume->ulBlockCount)
\r
102 || (pMB->uMaxNameLen != REDCONF_NAME_MAX)
\r
103 || (pMB->uDirectPointers != REDCONF_DIRECT_POINTERS)
\r
104 || (pMB->uIndirectPointers != REDCONF_INDIRECT_POINTERS)
\r
105 || (pMB->bBlockSizeP2 != BLOCK_SIZE_P2)
\r
106 || (((pMB->bFlags & MBFLAG_API_POSIX) != 0U) != (REDCONF_API_POSIX == 1))
\r
107 || (((pMB->bFlags & MBFLAG_INODE_TIMESTAMPS) != 0U) != (REDCONF_INODE_TIMESTAMPS == 1))
\r
108 || (((pMB->bFlags & MBFLAG_INODE_BLOCKS) != 0U) != (REDCONF_INODE_BLOCKS == 1)))
\r
112 #if REDCONF_API_POSIX == 1
\r
113 else if(((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U) != (REDCONF_API_POSIX_LINK == 1))
\r
118 else if((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U)
\r
125 /* Master block configuration is valid.
\r
127 Save the sequence number of the master block in the volume,
\r
128 since we need it later (see RedVolMountMetaroot()) and we do
\r
129 not want to re-buffer the master block.
\r
131 gpRedVolume->ullSequence = pMB->hdr.ullSequence;
\r
141 /** @brief Mount the latest metaroot.
\r
143 This function also populates the volume contexts.
\r
145 @return A negated ::REDSTATUS code indicating the operation result.
\r
147 @retval 0 Operation was successful.
\r
148 @retval -RED_EIO Both metaroots are missing or corrupt.
\r
150 REDSTATUS RedVolMountMetaroot(void)
\r
154 ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[0U]);
\r
158 ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[1U]);
\r
161 /* Determine which metaroot is the most recent copy that was written
\r
166 uint8_t bMR = UINT8_MAX;
\r
167 bool fSectorCRCIsValid;
\r
169 if(MetarootIsValid(&gpRedCoreVol->aMR[0U], &fSectorCRCIsValid))
\r
173 #ifdef REDCONF_ENDIAN_SWAP
\r
174 MetaRootEndianSwap(&gpRedCoreVol->aMR[0U]);
\r
177 else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)
\r
183 /* Metaroot is not valid, so it is ignored and there's nothing
\r
190 if(MetarootIsValid(&gpRedCoreVol->aMR[1U], &fSectorCRCIsValid))
\r
192 #ifdef REDCONF_ENDIAN_SWAP
\r
193 MetaRootEndianSwap(&gpRedCoreVol->aMR[1U]);
\r
196 if((bMR != 0U) || (gpRedCoreVol->aMR[1U].hdr.ullSequence > gpRedCoreVol->aMR[0U].hdr.ullSequence))
\r
201 else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)
\r
207 /* Metaroot is not valid, so it is ignored and there's nothing
\r
215 if(bMR == UINT8_MAX)
\r
217 /* Neither metaroot was valid.
\r
223 gpRedCoreVol->bCurMR = bMR;
\r
224 gpRedMR = &gpRedCoreVol->aMR[bMR];
\r
231 /* Normally the metaroot contains the highest sequence number, but the
\r
232 master block is the last block written during format, so on a
\r
233 freshly formatted volume the master block sequence number (stored in
\r
234 gpRedVolume->ullSequence) will be higher than that in the metaroot.
\r
236 if(gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence)
\r
238 gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence;
\r
241 /* gpRedVolume->ullSequence stores the *next* sequence number; to avoid
\r
242 giving the next node written to disk the same sequence number as the
\r
243 metaroot, increment it here.
\r
245 ret = RedVolSeqNumIncrement();
\r
250 gpRedVolume->fMounted = true;
\r
251 #if REDCONF_READ_ONLY == 0
\r
252 gpRedVolume->fReadOnly = false;
\r
255 #if RESERVED_BLOCKS > 0U
\r
256 gpRedCoreVol->fUseReservedBlocks = false;
\r
258 gpRedCoreVol->ulAlmostFreeBlocks = 0U;
\r
260 gpRedCoreVol->aMR[1U - gpRedCoreVol->bCurMR] = *gpRedMR;
\r
261 gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR;
\r
262 gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];
\r
269 /** @brief Determine whether the metaroot is valid.
\r
271 @param pMR The metaroot buffer.
\r
272 @param pfSectorCRCIsValid Populated with whether the first sector of the
\r
273 metaroot buffer is valid.
\r
275 @return Whether the metaroot is valid.
\r
277 @retval true The metaroot buffer is valid.
\r
278 @retval false The metaroot buffer is invalid.
\r
280 static bool MetarootIsValid(
\r
282 bool *pfSectorCRCIsValid)
\r
286 if(pfSectorCRCIsValid == NULL)
\r
290 else if(pMR == NULL)
\r
293 *pfSectorCRCIsValid = false;
\r
295 #ifdef REDCONF_ENDIAN_SWAP
\r
296 else if(RedRev32(pMR->hdr.ulSignature) != META_SIG_METAROOT)
\r
298 else if(pMR->hdr.ulSignature != META_SIG_METAROOT)
\r
301 *pfSectorCRCIsValid = false;
\r
305 const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMR);
\r
306 uint32_t ulSectorCRC = pMR->ulSectorCRC;
\r
309 #ifdef REDCONF_ENDIAN_SWAP
\r
310 ulSectorCRC = RedRev32(ulSectorCRC);
\r
313 /* The sector CRC was zero when the CRC was computed during the
\r
314 transaction, so it must be zero here.
\r
316 pMR->ulSectorCRC = 0U;
\r
318 ulCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);
\r
320 fRet = ulCRC == ulSectorCRC;
\r
321 *pfSectorCRCIsValid = fRet;
\r
325 if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)
\r
327 ulCRC = RedCrc32Update(ulCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);
\r
330 #ifdef REDCONF_ENDIAN_SWAP
\r
331 ulCRC = RedRev32(ulCRC);
\r
334 fRet = ulCRC == pMR->hdr.ulCRC;
\r
342 #if REDCONF_READ_ONLY == 0
\r
343 /** @brief Commit a transaction point.
\r
345 @return A negated ::REDSTATUS code indicating the operation result.
\r
347 @retval 0 Operation was successful.
\r
348 @retval -RED_EIO A disk I/O error occurred.
\r
350 REDSTATUS RedVolTransact(void)
\r
354 REDASSERT(!gpRedVolume->fReadOnly); /* Should be checked by caller. */
\r
356 if(gpRedCoreVol->fBranched)
\r
358 gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks;
\r
359 gpRedCoreVol->ulAlmostFreeBlocks = 0U;
\r
361 ret = RedBufferFlush(0U, gpRedVolume->ulBlockCount);
\r
365 gpRedMR->hdr.ulSignature = META_SIG_METAROOT;
\r
366 gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence;
\r
368 ret = RedVolSeqNumIncrement();
\r
373 const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(gpRedMR);
\r
374 uint32_t ulSectorCRC;
\r
376 #ifdef REDCONF_ENDIAN_SWAP
\r
377 MetaRootEndianSwap(gpRedMR);
\r
380 gpRedMR->ulSectorCRC = 0U;
\r
382 ulSectorCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);
\r
384 if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)
\r
386 gpRedMR->hdr.ulCRC = RedCrc32Update(ulSectorCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);
\r
390 gpRedMR->hdr.ulCRC = ulSectorCRC;
\r
393 gpRedMR->ulSectorCRC = ulSectorCRC;
\r
395 #ifdef REDCONF_ENDIAN_SWAP
\r
396 gpRedMR->hdr.ulCRC = RedRev32(gpRedMR->hdr.ulCRC);
\r
397 gpRedMR->ulSectorCRC = RedRev32(gpRedMR->ulSectorCRC);
\r
400 /* Flush the block device before writing the metaroot, so that all
\r
401 previously written blocks are guaranteed to be on the media before
\r
402 the metaroot is written. Otherwise, if the block device reorders
\r
403 the writes, the metaroot could reach the media before metadata it
\r
404 points at, creating a window for disk corruption if power is lost.
\r
406 ret = RedIoFlush(gbRedVolNum);
\r
411 ret = RedIoWrite(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR);
\r
413 #ifdef REDCONF_ENDIAN_SWAP
\r
414 MetaRootEndianSwap(gpRedMR);
\r
418 /* Flush the block device to force the metaroot write to the media. This
\r
419 guarantees the transaction point is really complete before we return.
\r
423 ret = RedIoFlush(gbRedVolNum);
\r
426 /* Toggle to the other metaroot buffer. The working state and committed
\r
427 state metaroot buffers exchange places.
\r
431 uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR;
\r
433 gpRedCoreVol->aMR[bNextMR] = *gpRedMR;
\r
434 gpRedCoreVol->bCurMR = bNextMR;
\r
436 gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];
\r
438 gpRedCoreVol->fBranched = false;
\r
441 CRITICAL_ASSERT(ret == 0);
\r
449 #ifdef REDCONF_ENDIAN_SWAP
\r
450 static void MetaRootEndianSwap(
\r
451 METAROOT *pMetaRoot)
\r
453 if(pMetaRoot == NULL)
\r
459 pMetaRoot->ulSectorCRC = RedRev32(pMetaRoot->ulSectorCRC);
\r
460 pMetaRoot->ulFreeBlocks = RedRev32(pMetaRoot->ulFreeBlocks);
\r
461 #if REDCONF_API_POSIX == 1
\r
462 pMetaRoot->ulFreeInodes = RedRev32(pMetaRoot->ulFreeInodes);
\r
464 pMetaRoot->ulAllocNextBlock = RedRev32(pMetaRoot->ulAllocNextBlock);
\r
470 /** @brief Process a critical file system error.
\r
472 @param pszFileName The file in which the error occurred.
\r
473 @param ulLineNum The line number at which the error occurred.
\r
475 void RedVolCriticalError(
\r
476 const char *pszFileName,
\r
477 uint32_t ulLineNum)
\r
479 #if REDCONF_OUTPUT == 1
\r
480 #if REDCONF_READ_ONLY == 0
\r
481 if(!gpRedVolume->fReadOnly)
\r
483 RedOsOutputString("Critical file system error in Reliance Edge, setting volume to READONLY\n");
\r
488 RedOsOutputString("Critical file system error in Reliance Edge (volume already READONLY)\n");
\r
492 #if REDCONF_READ_ONLY == 0
\r
493 gpRedVolume->fReadOnly = true;
\r
496 #if REDCONF_ASSERTS == 1
\r
497 RedOsAssertFail(pszFileName, ulLineNum);
\r
505 /** @brief Increment the sequence number.
\r
507 @return A negated ::REDSTATUS code indicating the operation result.
\r
509 @retval 0 Operation was successful.
\r
510 @retval -RED_EINVAL Cannot increment sequence number: maximum value reached.
\r
511 This should not ever happen.
\r
513 REDSTATUS RedVolSeqNumIncrement(void)
\r
517 if(gpRedVolume->ullSequence == UINT64_MAX)
\r
519 /* In practice this should never, ever happen; to get here, there would
\r
520 need to be UINT64_MAX disk writes, which would take eons: longer
\r
521 than the lifetime of any product or storage media. If this assert
\r
522 fires and the current year is still written with four digits,
\r
523 suspect memory corruption.
\r
530 gpRedVolume->ullSequence++;
\r