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 the block device buffering system.
\r
28 This module implements the block buffer cache. It has a number of block
\r
29 sized buffers which are used to store data from a given block (identified
\r
30 by both block number and volume number: this cache is shared among all
\r
31 volumes). Block buffers may be either dirty or clean. Most I/O passes
\r
32 through this module. When a buffer is needed for a block which is not in
\r
33 the cache, a "victim" is selected via a simple LRU scheme.
\r
36 #include <redcore.h>
\r
39 #if DINDIR_POINTERS > 0U
\r
40 #define INODE_META_BUFFERS 3U /* Inode, double indirect, indirect */
\r
41 #elif REDCONF_INDIRECT_POINTERS > 0U
\r
42 #define INODE_META_BUFFERS 2U /* Inode, indirect */
\r
43 #elif REDCONF_DIRECT_POINTERS == INODE_ENTRIES
\r
44 #define INODE_META_BUFFERS 1U /* Inode only */
\r
47 #define INODE_BUFFERS (INODE_META_BUFFERS + 1U) /* Add data buffer */
\r
49 #if REDCONF_IMAP_EXTERNAL == 1
\r
50 #define IMAP_BUFFERS 1U
\r
52 #define IMAP_BUFFERS 0U
\r
55 #if (REDCONF_READ_ONLY == 1) || (REDCONF_API_FSE == 1)
\r
56 /* Read, write, truncate, lookup: One inode all the way down, plus imap.
\r
58 #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + IMAP_BUFFERS)
\r
59 #elif REDCONF_API_POSIX == 1
\r
60 #if REDCONF_API_POSIX_RENAME == 1
\r
61 #if REDCONF_RENAME_ATOMIC == 1
\r
62 /* Two parent directories all the way down. Source and destination inode
\r
63 buffer. One inode buffer for cyclic rename detection. Imap. The
\r
64 parent inode buffers are released before deleting the destination
\r
65 inode, so that does not increase the minimum.
\r
67 #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 3U + IMAP_BUFFERS)
\r
69 /* Two parent directories all the way down. Source inode buffer. One
\r
70 inode buffer for cyclic rename detection. Imap.
\r
72 #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 2U + IMAP_BUFFERS)
\r
75 /* Link/create: Needs a parent inode all the way down, an extra inode
\r
76 buffer, and an imap buffer.
\r
78 Unlink is the same, since the parent inode buffers are released before
\r
79 the inode is deleted.
\r
81 #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + 1U + IMAP_BUFFERS)
\r
85 #if REDCONF_BUFFER_COUNT < MINIMUM_BUFFER_COUNT
\r
86 #error "REDCONF_BUFFER_COUNT is too low for the configuration"
\r
90 /* A note on the typecasts in the below macros: Operands to bitwise operators
\r
91 are subject to the "usual arithmetic conversions". This means that the
\r
92 flags, which have uint16_t values, are promoted to int. MISRA-C:2012 R10.1
\r
93 forbids using signed integers in bitwise operations, so we cast to uint32_t
\r
94 to avoid the integer promotion, then back to uint16_t to reflect the actual
\r
97 #define BFLAG_META_MASK (uint16_t)((uint32_t)BFLAG_META_MASTER | BFLAG_META_IMAP | BFLAG_META_INODE | BFLAG_META_INDIR | BFLAG_META_DINDIR)
\r
98 #define BFLAG_MASK (uint16_t)((uint32_t)BFLAG_DIRTY | BFLAG_NEW | BFLAG_META_MASK)
\r
101 /* An invalid block number. Used to indicate buffers which are not currently
\r
104 #define BBLK_INVALID UINT32_MAX
\r
107 /** @brief Metadata stored for each block buffer.
\r
109 To make better use of CPU caching when searching the BUFFERHEAD array, this
\r
110 structure should be kept small.
\r
114 uint32_t ulBlock; /**< Block number the buffer is associated with; BBLK_INVALID if unused. */
\r
115 uint8_t bVolNum; /**< Volume the block resides on. */
\r
116 uint8_t bRefCount; /**< Number of references. */
\r
117 uint16_t uFlags; /**< Buffer flags: mask of BFLAG_* values. */
\r
121 /** @brief State information for the block buffer module.
\r
125 /** Number of buffers which are referenced (have a bRefCount > 0).
\r
129 /** MRU array. Each element of the array stores a buffer index; each buffer
\r
130 index appears in the array once and only once. The first element of the
\r
131 array is the most-recently-used (MRU) buffer, followed by the next most
\r
132 recently used, and so on, till the last element, which is the least-
\r
133 recently-used (LRU) buffer.
\r
135 uint8_t abMRU[REDCONF_BUFFER_COUNT];
\r
137 /** Buffer heads, storing metadata for each buffer.
\r
139 BUFFERHEAD aHead[REDCONF_BUFFER_COUNT];
\r
141 /** Array of memory for the block buffers themselves.
\r
143 Force 64-bit alignment of the aabBuffer array to ensure that it is safe
\r
144 to cast buffer pointers to node structure pointers.
\r
146 ALIGNED_2D_BYTE_ARRAY(b, aabBuffer, REDCONF_BUFFER_COUNT, REDCONF_BLOCK_SIZE);
\r
150 static bool BufferIsValid(const uint8_t *pbBuffer, uint16_t uFlags);
\r
151 static bool BufferToIdx(const void *pBuffer, uint8_t *pbIdx);
\r
152 #if REDCONF_READ_ONLY == 0
\r
153 static REDSTATUS BufferWrite(uint8_t bIdx);
\r
154 static REDSTATUS BufferFinalize(uint8_t *pbBuffer, uint16_t uFlags);
\r
156 static void BufferMakeLRU(uint8_t bIdx);
\r
157 static void BufferMakeMRU(uint8_t bIdx);
\r
158 static bool BufferFind(uint32_t ulBlock, uint8_t *pbIdx);
\r
160 #ifdef REDCONF_ENDIAN_SWAP
\r
161 static void BufferEndianSwap(const void *pBuffer, uint16_t uFlags);
\r
162 static void BufferEndianSwapHeader(NODEHEADER *pHeader);
\r
163 static void BufferEndianSwapMaster(MASTERBLOCK *pMaster);
\r
164 static void BufferEndianSwapMetaRoot(METAROOT *pMetaRoot);
\r
165 static void BufferEndianSwapInode(INODE *pInode);
\r
166 static void BufferEndianSwapIndir(INDIR *pIndir);
\r
170 static BUFFERCTX gBufCtx;
\r
173 /** @brief Initialize the buffers.
\r
175 void RedBufferInit(void)
\r
179 RedMemSet(&gBufCtx, 0U, sizeof(gBufCtx));
\r
181 for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
\r
183 /* When the buffers have been freshly initialized, acquire the buffers
\r
184 in the order in which they appear in the array.
\r
186 gBufCtx.abMRU[bIdx] = (uint8_t)((REDCONF_BUFFER_COUNT - bIdx) - 1U);
\r
187 gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;
\r
192 /** @brief Acquire a buffer.
\r
194 @param ulBlock Block number to acquire.
\r
195 @param uFlags BFLAG_ values for the operation.
\r
196 @param ppBuffer On success, populated with the acquired buffer.
\r
198 @return A negated ::REDSTATUS code indicating the operation result.
\r
200 @retval 0 Operation was successful.
\r
201 @retval -RED_EIO A disk I/O error occurred.
\r
202 @retval -RED_EINVAL Invalid parameters.
\r
203 @retval -RED_EBUSY All buffers are referenced.
\r
205 REDSTATUS RedBufferGet(
\r
213 if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL))
\r
220 if(BufferFind(ulBlock, &bIdx))
\r
222 /* Error if the buffer exists and BFLAG_NEW was specified, since
\r
223 the new flag is used when a block is newly allocated/created, so
\r
224 the block was previously free and and there should never be an
\r
225 existing buffer for a free block.
\r
227 Error if the buffer exists but does not have the same type as
\r
230 if( ((uFlags & BFLAG_NEW) != 0U)
\r
231 || ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK)))
\r
237 else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT)
\r
239 /* The MINIMUM_BUFFER_COUNT is supposed to ensure that no operation
\r
240 ever runs out of buffers, so this should never happen.
\r
249 /* Search for the least recently used buffer which is not
\r
252 for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--)
\r
254 if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U)
\r
260 bIdx = gBufCtx.abMRU[bIdx];
\r
261 pHead = &gBufCtx.aHead[bIdx];
\r
263 if(pHead->bRefCount == 0U)
\r
265 /* If the LRU buffer is valid and dirty, write it out before
\r
268 if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID))
\r
270 #if REDCONF_READ_ONLY == 1
\r
274 ret = BufferWrite(bIdx);
\r
280 /* All the buffers are used, which should have been caught by
\r
281 checking gBufCtx.uNumUsed.
\r
289 if((uFlags & BFLAG_NEW) == 0U)
\r
291 /* Invalidate the LRU buffer. If the read fails, we do not
\r
292 want the buffer head to continue to refer to the old
\r
293 block number, since the read, even if it fails, may have
\r
294 partially overwritten the buffer data (consider the case
\r
295 where block size exceeds sector size, and some but not
\r
296 all of the sectors are read successfully), and if the
\r
297 buffer were to be used subsequently with its partially
\r
298 erroneous contents, bad things could happen.
\r
300 pHead->ulBlock = BBLK_INVALID;
\r
302 ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);
\r
304 if((ret == 0) && ((uFlags & BFLAG_META) != 0U))
\r
306 if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags))
\r
308 /* A corrupt metadata node is usually a critical
\r
309 error. The master block is an exception since
\r
310 it might be invalid because the volume is not
\r
311 mounted; that condition is expected and should
\r
312 not result in an assertion.
\r
314 CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) != 0U);
\r
319 #ifdef REDCONF_ENDIAN_SWAP
\r
322 BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags);
\r
328 RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE);
\r
334 pHead->bVolNum = gbRedVolNum;
\r
335 pHead->ulBlock = ulBlock;
\r
336 pHead->uFlags = 0U;
\r
340 /* Reference the buffer, update its flags, and promote it to MRU. This
\r
341 happens both when BufferFind() found an existing buffer for the
\r
342 block and when the LRU buffer was repurposed to create a buffer for
\r
347 BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
349 pHead->bRefCount++;
\r
351 if(pHead->bRefCount == 1U)
\r
353 gBufCtx.uNumUsed++;
\r
356 /* BFLAG_NEW tells this function to zero the buffer instead of
\r
357 reading it from disk; it has no meaning later on, and thus is
\r
360 pHead->uFlags |= (uFlags & (~BFLAG_NEW));
\r
362 BufferMakeMRU(bIdx);
\r
364 *ppBuffer = gBufCtx.b.aabBuffer[bIdx];
\r
372 /** @brief Release a buffer.
\r
374 @param pBuffer The buffer to release.
\r
377 const void *pBuffer)
\r
381 if(!BufferToIdx(pBuffer, &bIdx))
\r
387 REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);
\r
388 gBufCtx.aHead[bIdx].bRefCount--;
\r
390 if(gBufCtx.aHead[bIdx].bRefCount == 0U)
\r
392 REDASSERT(gBufCtx.uNumUsed > 0U);
\r
393 gBufCtx.uNumUsed--;
\r
399 #if REDCONF_READ_ONLY == 0
\r
400 /** @brief Flush all buffers for the active volume in the given range of blocks.
\r
402 @param ulBlockStart Starting block number to flush.
\r
403 @param ulBlockCount Count of blocks, starting at @p ulBlockStart, to flush.
\r
406 @return A negated ::REDSTATUS code indicating the operation result.
\r
408 @retval 0 Operation was successful.
\r
409 @retval -RED_EIO A disk I/O error occurred.
\r
410 @retval -RED_EINVAL Invalid parameters.
\r
412 REDSTATUS RedBufferFlush(
\r
413 uint32_t ulBlockStart,
\r
414 uint32_t ulBlockCount)
\r
418 if( (ulBlockStart >= gpRedVolume->ulBlockCount)
\r
419 || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)
\r
420 || (ulBlockCount == 0U))
\r
429 for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
\r
431 BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
433 if( (pHead->bVolNum == gbRedVolNum)
\r
434 && (pHead->ulBlock != BBLK_INVALID)
\r
435 && ((pHead->uFlags & BFLAG_DIRTY) != 0U)
\r
436 && (pHead->ulBlock >= ulBlockStart)
\r
437 && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))
\r
439 ret = BufferWrite(bIdx);
\r
443 pHead->uFlags &= (~BFLAG_DIRTY);
\r
457 /** @brief Mark a buffer dirty
\r
459 @param pBuffer The buffer to mark dirty.
\r
461 void RedBufferDirty(
\r
462 const void *pBuffer)
\r
466 if(!BufferToIdx(pBuffer, &bIdx))
\r
472 REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);
\r
474 gBufCtx.aHead[bIdx].uFlags |= BFLAG_DIRTY;
\r
479 /** @brief Branch a buffer, marking it dirty and assigning a new block number.
\r
481 @param pBuffer The buffer to branch.
\r
482 @param ulBlockNew The new block number for the buffer.
\r
484 void RedBufferBranch(
\r
485 const void *pBuffer,
\r
486 uint32_t ulBlockNew)
\r
490 if( !BufferToIdx(pBuffer, &bIdx)
\r
491 || (ulBlockNew >= gpRedVolume->ulBlockCount))
\r
497 BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
499 REDASSERT(pHead->bRefCount > 0U);
\r
500 REDASSERT((pHead->uFlags & BFLAG_DIRTY) == 0U);
\r
502 pHead->uFlags |= BFLAG_DIRTY;
\r
503 pHead->ulBlock = ulBlockNew;
\r
508 #if (REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED
\r
509 /** @brief Discard a buffer, releasing it and marking it invalid.
\r
511 @param pBuffer The buffer to discard.
\r
513 void RedBufferDiscard(
\r
514 const void *pBuffer)
\r
518 if(!BufferToIdx(pBuffer, &bIdx))
\r
524 REDASSERT(gBufCtx.aHead[bIdx].bRefCount == 1U);
\r
525 REDASSERT(gBufCtx.uNumUsed > 0U);
\r
527 gBufCtx.aHead[bIdx].bRefCount = 0U;
\r
528 gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;
\r
530 gBufCtx.uNumUsed--;
\r
532 BufferMakeLRU(bIdx);
\r
536 #endif /* REDCONF_READ_ONLY == 0 */
\r
539 /** @brief Discard a range of buffers, marking them invalid.
\r
541 @param ulBlockStart The starting block number to discard
\r
542 @param ulBlockCount The number of blocks, starting at @p ulBlockStart, to
\r
543 discard. Must not be zero.
\r
545 @return A negated ::REDSTATUS code indicating the operation result.
\r
547 @retval 0 Operation was successful.
\r
548 @retval -RED_EINVAL Invalid parameters.
\r
549 @retval -RED_EBUSY A block in the desired range is referenced.
\r
551 REDSTATUS RedBufferDiscardRange(
\r
552 uint32_t ulBlockStart,
\r
553 uint32_t ulBlockCount)
\r
557 if( (ulBlockStart >= gpRedVolume->ulBlockCount)
\r
558 || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)
\r
559 || (ulBlockCount == 0U))
\r
568 for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
\r
570 BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
572 if( (pHead->bVolNum == gbRedVolNum)
\r
573 && (pHead->ulBlock != BBLK_INVALID)
\r
574 && (pHead->ulBlock >= ulBlockStart)
\r
575 && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))
\r
577 if(pHead->bRefCount == 0U)
\r
579 pHead->ulBlock = BBLK_INVALID;
\r
581 BufferMakeLRU(bIdx);
\r
585 /* This should never happen. There are three general cases
\r
586 when this function is used:
\r
588 1) Discarding every block, as happens during unmount
\r
589 and at the end of format. There should no longer be
\r
590 any referenced buffers at those points.
\r
591 2) Discarding a block which has become free. All
\r
592 buffers for such blocks should be put or branched
\r
594 3) Discarding of blocks that were just written straight
\r
595 to disk, leaving stale data in the buffer. The write
\r
596 code should never reference buffers for these blocks,
\r
597 since they would not be needed or used.
\r
611 /** Determine whether a metadata buffer is valid.
\r
613 This includes checking its signature, CRC, and sequence number.
\r
615 @param pbBuffer Pointer to the metadata buffer to validate.
\r
616 @param uFlags The buffer flags provided by the caller. Used to determine
\r
617 the expected signature.
\r
619 @return Whether the metadata buffer is valid.
\r
621 @retval true The metadata buffer is valid.
\r
622 @retval false The metadata buffer is invalid.
\r
624 static bool BufferIsValid(
\r
625 const uint8_t *pbBuffer,
\r
630 if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
\r
638 uint16_t uMetaFlags = uFlags & BFLAG_META_MASK;
\r
640 /* Casting pbBuffer to (NODEHEADER *) would run afoul MISRA-C:2012
\r
641 R11.3, so instead copy the fields out.
\r
643 RedMemCpy(&buf.ulSignature, &pbBuffer[NODEHEADER_OFFSET_SIG], sizeof(buf.ulSignature));
\r
644 RedMemCpy(&buf.ulCRC, &pbBuffer[NODEHEADER_OFFSET_CRC], sizeof(buf.ulCRC));
\r
645 RedMemCpy(&buf.ullSequence, &pbBuffer[NODEHEADER_OFFSET_SEQ], sizeof(buf.ullSequence));
\r
647 #ifdef REDCONF_ENDIAN_SWAP
\r
648 buf.ulCRC = RedRev32(buf.ulCRC);
\r
649 buf.ulSignature = RedRev32(buf.ulSignature);
\r
650 buf.ullSequence = RedRev64(buf.ullSequence);
\r
653 /* Make sure the signature is correct for the type of metadata block
\r
654 requested by the caller.
\r
656 switch(buf.ulSignature)
\r
658 case META_SIG_MASTER:
\r
659 fValid = (uMetaFlags == BFLAG_META_MASTER);
\r
661 #if REDCONF_IMAP_EXTERNAL == 1
\r
662 case META_SIG_IMAP:
\r
663 fValid = (uMetaFlags == BFLAG_META_IMAP);
\r
666 case META_SIG_INODE:
\r
667 fValid = (uMetaFlags == BFLAG_META_INODE);
\r
669 #if DINDIR_POINTERS > 0U
\r
670 case META_SIG_DINDIR:
\r
671 fValid = (uMetaFlags == BFLAG_META_DINDIR);
\r
674 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
\r
675 case META_SIG_INDIR:
\r
676 fValid = (uMetaFlags == BFLAG_META_INDIR);
\r
686 uint32_t ulComputedCrc;
\r
688 /* Check for disk corruption by comparing the stored CRC with one
\r
689 computed from the data.
\r
691 Also check the sequence number: if it is greater than the
\r
692 current sequence number, the block is from a previous format
\r
693 or the disk is writing blocks out of order. During mount,
\r
694 before the metaroots have been read, the sequence number will
\r
695 be unknown, and the check is skipped.
\r
697 ulComputedCrc = RedCrcNode(pbBuffer);
\r
698 if(buf.ulCRC != ulComputedCrc)
\r
702 else if(gpRedVolume->fMounted && (buf.ullSequence >= gpRedVolume->ullSequence))
\r
708 /* Buffer is valid. No action, fValid is already true.
\r
718 /** @brief Derive the index of the buffer.
\r
720 @param pBuffer The buffer to derive the index of.
\r
721 @param pbIdx On success, populated with the index of the buffer.
\r
723 @return Boolean indicating result.
\r
725 @retval true Success.
\r
726 @retval false Failure. @p pBuffer is not a valid buffer pointer.
\r
728 static bool BufferToIdx(
\r
729 const void *pBuffer,
\r
734 if((pBuffer != NULL) && (pbIdx != NULL))
\r
738 /* pBuffer should be a pointer to one of the block buffers.
\r
740 A good compiler should optimize this loop into a bounds check and an
\r
741 alignment check, although GCC has been observed to not do so; if the
\r
742 number of buffers is small, it should not make much difference. The
\r
743 alternative is to use pointer comparisons, but this both deviates
\r
744 from MISRA-C:2012 and involves undefined behavior.
\r
746 for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
\r
748 if(pBuffer == &gBufCtx.b.aabBuffer[bIdx][0U])
\r
754 if( (bIdx < REDCONF_BUFFER_COUNT)
\r
755 && (gBufCtx.aHead[bIdx].ulBlock != BBLK_INVALID)
\r
756 && (gBufCtx.aHead[bIdx].bVolNum == gbRedVolNum))
\r
767 #if REDCONF_READ_ONLY == 0
\r
768 /** @brief Write out a dirty buffer.
\r
770 @param bIdx The index of the buffer to write.
\r
772 @return A negated ::REDSTATUS code indicating the operation result.
\r
774 @retval 0 Operation was successful.
\r
775 @retval -RED_EIO A disk I/O error occurred.
\r
776 @retval -RED_EINVAL Invalid parameters.
\r
778 static REDSTATUS BufferWrite(
\r
783 if(bIdx < REDCONF_BUFFER_COUNT)
\r
785 const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
787 REDASSERT((pHead->uFlags & BFLAG_DIRTY) != 0U);
\r
789 if((pHead->uFlags & BFLAG_META) != 0U)
\r
791 ret = BufferFinalize(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);
\r
796 ret = RedIoWrite(pHead->bVolNum, pHead->ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);
\r
798 #ifdef REDCONF_ENDIAN_SWAP
\r
799 BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);
\r
813 /** @brief Finalize a metadata buffer.
\r
815 This updates the CRC and the sequence number. It also sets the signature,
\r
816 though this is only truly needed if the buffer is new.
\r
818 @param pbBuffer Pointer to the metadata buffer to finalize.
\r
819 @param uFlags The associated buffer flags. Used to determine the expected
\r
822 @return A negated ::REDSTATUS code indicating the operation result.
\r
824 @retval 0 Operation was successful.
\r
825 @retval -RED_EINVAL Invalid parameter; or maximum sequence number reached.
\r
827 static REDSTATUS BufferFinalize(
\r
833 if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
\r
840 uint32_t ulSignature;
\r
842 switch(uFlags & BFLAG_META_MASK)
\r
844 case BFLAG_META_MASTER:
\r
845 ulSignature = META_SIG_MASTER;
\r
847 #if REDCONF_IMAP_EXTERNAL == 1
\r
848 case BFLAG_META_IMAP:
\r
849 ulSignature = META_SIG_IMAP;
\r
852 case BFLAG_META_INODE:
\r
853 ulSignature = META_SIG_INODE;
\r
855 #if DINDIR_POINTERS > 0U
\r
856 case BFLAG_META_DINDIR:
\r
857 ulSignature = META_SIG_DINDIR;
\r
860 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
\r
861 case BFLAG_META_INDIR:
\r
862 ulSignature = META_SIG_INDIR;
\r
870 if(ulSignature == 0U)
\r
877 uint64_t ullSeqNum = gpRedVolume->ullSequence;
\r
879 ret = RedVolSeqNumIncrement();
\r
884 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SIG], &ulSignature, sizeof(ulSignature));
\r
885 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SEQ], &ullSeqNum, sizeof(ullSeqNum));
\r
887 #ifdef REDCONF_ENDIAN_SWAP
\r
888 BufferEndianSwap(pbBuffer, uFlags);
\r
891 ulCrc = RedCrcNode(pbBuffer);
\r
892 #ifdef REDCONF_ENDIAN_SWAP
\r
893 ulCrc = RedRev32(ulCrc);
\r
895 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_CRC], &ulCrc, sizeof(ulCrc));
\r
902 #endif /* REDCONF_READ_ONLY == 0 */
\r
905 #ifdef REDCONF_ENDIAN_SWAP
\r
906 /** @brief Swap the byte order of a metadata buffer
\r
908 Does nothing if the buffer is not a metadata node. Also does nothing for
\r
909 meta roots, which don't go through the buffers anyways.
\r
911 @param pBuffer Pointer to the metadata buffer to swap
\r
912 @param uFlags The associated buffer flags. Used to determin the type of
\r
915 static void BufferEndianSwap(
\r
919 if((pBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
\r
923 else if((uFlags & BFLAG_META_MASK) != 0)
\r
925 BufferEndianSwapHeader(pBuffer);
\r
927 switch(uFlags & BFLAG_META_MASK)
\r
929 case BFLAG_META_MASTER:
\r
930 BufferEndianSwapMaster(pBuffer);
\r
932 case BFLAG_META_INODE:
\r
933 BufferEndianSwapInode(pBuffer);
\r
935 #if DINDIR_POINTERS > 0U
\r
936 case BFLAG_META_DINDIR:
\r
937 BufferEndianSwapIndir(pBuffer);
\r
940 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
\r
941 case BFLAG_META_INDIR:
\r
942 BufferEndianSwapIndir(pBuffer);
\r
951 /* File data buffers do not need to be swapped.
\r
957 /** @brief Swap the byte order of a metadata node header
\r
959 @param pHeader Pointer to the metadata node header to swap
\r
961 static void BufferEndianSwapHeader(
\r
962 NODEHEADER *pHeader)
\r
964 if(pHeader == NULL)
\r
970 pHeader->ulSignature = RedRev32(pHeader->ulSignature);
\r
971 pHeader->ulCRC = RedRev32(pHeader->ulCRC);
\r
972 pHeader->ullSequence = RedRev64(pHeader->ullSequence);
\r
977 /** @brief Swap the byte order of a master block
\r
979 @param pMaster Pointer to the master block to swap
\r
981 static void BufferEndianSwapMaster(
\r
982 MASTERBLOCK *pMaster)
\r
984 if(pMaster == NULL)
\r
990 pMaster->ulVersion = RedRev32(pMaster->ulVersion);
\r
991 pMaster->ulFormatTime = RedRev32(pMaster->ulFormatTime);
\r
992 pMaster->ulInodeCount = RedRev32(pMaster->ulInodeCount);
\r
993 pMaster->ulBlockCount = RedRev32(pMaster->ulBlockCount);
\r
994 pMaster->uMaxNameLen = RedRev16(pMaster->uMaxNameLen);
\r
995 pMaster->uDirectPointers = RedRev16(pMaster->uDirectPointers);
\r
996 pMaster->uIndirectPointers = RedRev16(pMaster->uIndirectPointers);
\r
1001 /** @brief Swap the byte order of an inode
\r
1003 @param pInode Pointer to the inode to swap
\r
1005 static void BufferEndianSwapInode(
\r
1008 if(pInode == NULL)
\r
1016 pInode->ullSize = RedRev64(pInode->ullSize);
\r
1018 #if REDCONF_INODE_BLOCKS == 1
\r
1019 pInode->ulBlocks = RedRev32(pInode->ulBlocks);
\r
1022 #if REDCONF_INODE_TIMESTAMPS == 1
\r
1023 pInode->ulATime = RedRev32(pInode->ulATime);
\r
1024 pInode->ulMTime = RedRev32(pInode->ulMTime);
\r
1025 pInode->ulCTime = RedRev32(pInode->ulCTime);
\r
1028 pInode->uMode = RedRev16(pInode->uMode);
\r
1030 #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)
\r
1031 pInode->uNLink = RedRev16(pInode->uNLink);
\r
1034 #if REDCONF_API_POSIX == 1
\r
1035 pInode->ulPInode = RedRev32(pInode->ulPInode);
\r
1038 for(ulIdx = 0; ulIdx < INODE_ENTRIES; ulIdx++)
\r
1040 pInode->aulEntries[ulIdx] = RedRev32(pInode->aulEntries[ulIdx]);
\r
1046 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
\r
1047 /** @brief Swap the byte order of an indirect or double indirect node
\r
1049 @param pIndir Pointer to the node to swap
\r
1051 static void BufferEndianSwapIndir(
\r
1054 if(pIndir == NULL)
\r
1062 pIndir->ulInode = RedRev32(pIndir->ulInode);
\r
1064 for(ulIdx = 0; ulIdx < INDIR_ENTRIES; ulIdx++)
\r
1066 pIndir->aulEntries[ulIdx] = RedRev32(pIndir->aulEntries[ulIdx]);
\r
1071 #endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
\r
1072 #endif /* #ifdef REDCONF_ENDIAN_SWAP */
\r
1075 /** @brief Mark a buffer as least recently used.
\r
1077 @param bIdx The index of the buffer to make LRU.
\r
1079 static void BufferMakeLRU(
\r
1082 if(bIdx >= REDCONF_BUFFER_COUNT)
\r
1086 else if(bIdx != gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U])
\r
1090 /* Find the current position of the buffer in the MRU array. We do not
\r
1091 need to check the last slot, since we already know from the above
\r
1092 check that the index is not there.
\r
1094 for(bMruIdx = 0U; bMruIdx < (REDCONF_BUFFER_COUNT - 1U); bMruIdx++)
\r
1096 if(bIdx == gBufCtx.abMRU[bMruIdx])
\r
1102 if(bMruIdx < (REDCONF_BUFFER_COUNT - 1U))
\r
1104 /* Move the buffer index to the back of the MRU array, making it
\r
1107 RedMemMove(&gBufCtx.abMRU[bMruIdx], &gBufCtx.abMRU[bMruIdx + 1U], REDCONF_BUFFER_COUNT - ((uint32_t)bMruIdx + 1U));
\r
1108 gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U] = bIdx;
\r
1117 /* Buffer already LRU, nothing to do.
\r
1123 /** @brief Mark a buffer as most recently used.
\r
1125 @param bIdx The index of the buffer to make MRU.
\r
1127 static void BufferMakeMRU(
\r
1130 if(bIdx >= REDCONF_BUFFER_COUNT)
\r
1134 else if(bIdx != gBufCtx.abMRU[0U])
\r
1138 /* Find the current position of the buffer in the MRU array. We do not
\r
1139 need to check the first slot, since we already know from the above
\r
1140 check that the index is not there.
\r
1142 for(bMruIdx = 1U; bMruIdx < REDCONF_BUFFER_COUNT; bMruIdx++)
\r
1144 if(bIdx == gBufCtx.abMRU[bMruIdx])
\r
1150 if(bMruIdx < REDCONF_BUFFER_COUNT)
\r
1152 /* Move the buffer index to the front of the MRU array, making it
\r
1155 RedMemMove(&gBufCtx.abMRU[1U], &gBufCtx.abMRU[0U], bMruIdx);
\r
1156 gBufCtx.abMRU[0U] = bIdx;
\r
1165 /* Buffer already MRU, nothing to do.
\r
1171 /** @brief Find a block in the buffers.
\r
1173 @param ulBlock The block number to find.
\r
1174 @param pbIdx If the block is buffered (true is returned), populated with
\r
1175 the index of the buffer.
\r
1177 @return Boolean indicating whether or not the block is buffered.
\r
1179 @retval true @p ulBlock is buffered, and its index has been stored in
\r
1181 @retval false @p ulBlock is not buffered.
\r
1183 static bool BufferFind(
\r
1189 if((ulBlock >= gpRedVolume->ulBlockCount) || (pbIdx == NULL))
\r
1197 for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
\r
1199 const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
\r
1201 if((pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock == ulBlock))
\r