]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c
Update version numbers in preparation for new release.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / core / driver / buffer.c
1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
2 \r
3                    Copyright (c) 2014-2015 Datalight, Inc.\r
4                        All Rights Reserved Worldwide.\r
5 \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
9 \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
14 \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
18 */\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
23     more information.\r
24 */\r
25 /** @file\r
26     @brief Implements the block device buffering system.\r
27 \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
34 */\r
35 #include <redfs.h>\r
36 #include <redcore.h>\r
37 \r
38 \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
45 #endif\r
46 \r
47 #define INODE_BUFFERS (INODE_META_BUFFERS + 1U) /* Add data buffer */\r
48 \r
49 #if REDCONF_IMAP_EXTERNAL == 1\r
50   #define IMAP_BUFFERS 1U\r
51 #else\r
52   #define IMAP_BUFFERS 0U\r
53 #endif\r
54 \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
57   */\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
66       */\r
67       #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 3U + IMAP_BUFFERS)\r
68     #else\r
69       /*  Two parent directories all the way down.  Source inode buffer.  One\r
70           inode buffer for cyclic rename detection.  Imap.\r
71       */\r
72       #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 2U + IMAP_BUFFERS)\r
73     #endif\r
74   #else\r
75     /*  Link/create: Needs a parent inode all the way down, an extra inode\r
76         buffer, and an imap buffer.\r
77 \r
78         Unlink is the same, since the parent inode buffers are released before\r
79         the inode is deleted.\r
80     */\r
81     #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + 1U + IMAP_BUFFERS)\r
82   #endif\r
83 #endif\r
84 \r
85 #if REDCONF_BUFFER_COUNT < MINIMUM_BUFFER_COUNT\r
86 #error "REDCONF_BUFFER_COUNT is too low for the configuration"\r
87 #endif\r
88 \r
89 \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
95     type.\r
96 */\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
99 \r
100 \r
101 /*  An invalid block number.  Used to indicate buffers which are not currently\r
102     in use.\r
103 */\r
104 #define BBLK_INVALID UINT32_MAX\r
105 \r
106 \r
107 /** @brief Metadata stored for each block buffer.\r
108 \r
109     To make better use of CPU caching when searching the BUFFERHEAD array, this\r
110     structure should be kept small.\r
111 */\r
112 typedef struct\r
113 {\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
118 } BUFFERHEAD;\r
119 \r
120 \r
121 /** @brief State information for the block buffer module.\r
122 */\r
123 typedef struct\r
124 {\r
125     /** Number of buffers which are referenced (have a bRefCount > 0).\r
126     */\r
127     uint16_t    uNumUsed;\r
128 \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
134     */\r
135     uint8_t     abMRU[REDCONF_BUFFER_COUNT];\r
136 \r
137     /** Buffer heads, storing metadata for each buffer.\r
138     */\r
139     BUFFERHEAD  aHead[REDCONF_BUFFER_COUNT];\r
140 \r
141     /** Array of memory for the block buffers themselves.\r
142 \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
145     */\r
146     ALIGNED_2D_BYTE_ARRAY(b, aabBuffer, REDCONF_BUFFER_COUNT, REDCONF_BLOCK_SIZE);\r
147 } BUFFERCTX;\r
148 \r
149 \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
155 #endif\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
159 \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
167 #endif\r
168 \r
169 \r
170 static BUFFERCTX gBufCtx;\r
171 \r
172 \r
173 /** @brief Initialize the buffers.\r
174 */\r
175 void RedBufferInit(void)\r
176 {\r
177     uint8_t bIdx;\r
178 \r
179     RedMemSet(&gBufCtx, 0U, sizeof(gBufCtx));\r
180 \r
181     for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
182     {\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
185         */\r
186         gBufCtx.abMRU[bIdx] = (uint8_t)((REDCONF_BUFFER_COUNT - bIdx) - 1U);\r
187         gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;\r
188     }\r
189 }\r
190 \r
191 \r
192 /** @brief Acquire a buffer.\r
193 \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
197 \r
198     @return A negated ::REDSTATUS code indicating the operation result.\r
199 \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
204 */\r
205 REDSTATUS RedBufferGet(\r
206     uint32_t    ulBlock,\r
207     uint16_t    uFlags,\r
208     void      **ppBuffer)\r
209 {\r
210     REDSTATUS   ret = 0;\r
211     uint8_t     bIdx;\r
212 \r
213     if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL))\r
214     {\r
215         REDERROR();\r
216         ret = -RED_EINVAL;\r
217     }\r
218     else\r
219     {\r
220         if(BufferFind(ulBlock, &bIdx))\r
221         {\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
226 \r
227                 Error if the buffer exists but does not have the same type as\r
228                 was requested.\r
229             */\r
230             if(    ((uFlags & BFLAG_NEW) != 0U)\r
231                 || ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK)))\r
232             {\r
233                 CRITICAL_ERROR();\r
234                 ret = -RED_EFUBAR;\r
235             }\r
236         }\r
237         else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT)\r
238         {\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
241             */\r
242             CRITICAL_ERROR();\r
243             ret = -RED_EBUSY;\r
244         }\r
245         else\r
246         {\r
247             BUFFERHEAD *pHead;\r
248 \r
249             /*  Search for the least recently used buffer which is not\r
250                 referenced.\r
251             */\r
252             for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--)\r
253             {\r
254                 if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U)\r
255                 {\r
256                     break;\r
257                 }\r
258             }\r
259 \r
260             bIdx = gBufCtx.abMRU[bIdx];\r
261             pHead = &gBufCtx.aHead[bIdx];\r
262 \r
263             if(pHead->bRefCount == 0U)\r
264             {\r
265                 /*  If the LRU buffer is valid and dirty, write it out before\r
266                     repurposing it.\r
267                 */\r
268                 if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID))\r
269                 {\r
270                   #if REDCONF_READ_ONLY == 1\r
271                     CRITICAL_ERROR();\r
272                     ret = -RED_EFUBAR;\r
273                   #else\r
274                     ret = BufferWrite(bIdx);\r
275                   #endif\r
276                 }\r
277             }\r
278             else\r
279             {\r
280                 /*  All the buffers are used, which should have been caught by\r
281                     checking gBufCtx.uNumUsed.\r
282                 */\r
283                 CRITICAL_ERROR();\r
284                 ret = -RED_EBUSY;\r
285             }\r
286 \r
287             if(ret == 0)\r
288             {\r
289                 if((uFlags & BFLAG_NEW) == 0U)\r
290                 {\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
299                     */\r
300                     pHead->ulBlock = BBLK_INVALID;\r
301 \r
302                     ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);\r
303 \r
304                     if((ret == 0) && ((uFlags & BFLAG_META) != 0U))\r
305                     {\r
306                         if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags))\r
307                         {\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
313                             */\r
314                             CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) != 0U);\r
315                             ret = -RED_EIO;\r
316                         }\r
317                     }\r
318 \r
319                   #ifdef REDCONF_ENDIAN_SWAP\r
320                     if(ret == 0)\r
321                     {\r
322                         BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags);\r
323                     }\r
324                   #endif\r
325                 }\r
326                 else\r
327                 {\r
328                     RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE);\r
329                 }\r
330             }\r
331 \r
332             if(ret == 0)\r
333             {\r
334                 pHead->bVolNum = gbRedVolNum;\r
335                 pHead->ulBlock = ulBlock;\r
336                 pHead->uFlags = 0U;\r
337             }\r
338         }\r
339 \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
343             the block.\r
344         */\r
345         if(ret == 0)\r
346         {\r
347             BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
348 \r
349             pHead->bRefCount++;\r
350 \r
351             if(pHead->bRefCount == 1U)\r
352             {\r
353                 gBufCtx.uNumUsed++;\r
354             }\r
355 \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
358                 not saved.\r
359             */\r
360             pHead->uFlags |= (uFlags & (~BFLAG_NEW));\r
361 \r
362             BufferMakeMRU(bIdx);\r
363 \r
364             *ppBuffer = gBufCtx.b.aabBuffer[bIdx];\r
365         }\r
366     }\r
367 \r
368     return ret;\r
369 }\r
370 \r
371 \r
372 /** @brief Release a buffer.\r
373 \r
374     @param pBuffer  The buffer to release.\r
375  */\r
376 void RedBufferPut(\r
377     const void *pBuffer)\r
378 {\r
379     uint8_t     bIdx;\r
380 \r
381     if(!BufferToIdx(pBuffer, &bIdx))\r
382     {\r
383         REDERROR();\r
384     }\r
385     else\r
386     {\r
387         REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);\r
388         gBufCtx.aHead[bIdx].bRefCount--;\r
389 \r
390         if(gBufCtx.aHead[bIdx].bRefCount == 0U)\r
391         {\r
392             REDASSERT(gBufCtx.uNumUsed > 0U);\r
393             gBufCtx.uNumUsed--;\r
394         }\r
395     }\r
396 }\r
397 \r
398 \r
399 #if REDCONF_READ_ONLY == 0\r
400 /** @brief Flush all buffers for the active volume in the given range of blocks.\r
401 \r
402     @param ulBlockStart Starting block number to flush.\r
403     @param ulBlockCount Count of blocks, starting at @p ulBlockStart, to flush.\r
404                         Must not be zero.\r
405 \r
406     @return A negated ::REDSTATUS code indicating the operation result.\r
407 \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
411 */\r
412 REDSTATUS RedBufferFlush(\r
413     uint32_t    ulBlockStart,\r
414     uint32_t    ulBlockCount)\r
415 {\r
416     REDSTATUS   ret = 0;\r
417 \r
418     if(    (ulBlockStart >= gpRedVolume->ulBlockCount)\r
419         || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)\r
420         || (ulBlockCount == 0U))\r
421     {\r
422         REDERROR();\r
423         ret = -RED_EINVAL;\r
424     }\r
425     else\r
426     {\r
427         uint8_t bIdx;\r
428 \r
429         for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
430         {\r
431             BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
432 \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
438             {\r
439                 ret = BufferWrite(bIdx);\r
440 \r
441                 if(ret == 0)\r
442                 {\r
443                     pHead->uFlags &= (~BFLAG_DIRTY);\r
444                 }\r
445                 else\r
446                 {\r
447                     break;\r
448                 }\r
449             }\r
450         }\r
451     }\r
452 \r
453     return ret;\r
454 }\r
455 \r
456 \r
457 /** @brief Mark a buffer dirty\r
458 \r
459     @param pBuffer  The buffer to mark dirty.\r
460 */\r
461 void RedBufferDirty(\r
462     const void *pBuffer)\r
463 {\r
464     uint8_t     bIdx;\r
465 \r
466     if(!BufferToIdx(pBuffer, &bIdx))\r
467     {\r
468         REDERROR();\r
469     }\r
470     else\r
471     {\r
472         REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);\r
473 \r
474         gBufCtx.aHead[bIdx].uFlags |= BFLAG_DIRTY;\r
475     }\r
476 }\r
477 \r
478 \r
479 /** @brief Branch a buffer, marking it dirty and assigning a new block number.\r
480 \r
481     @param pBuffer      The buffer to branch.\r
482     @param ulBlockNew   The new block number for the buffer.\r
483 */\r
484 void RedBufferBranch(\r
485     const void *pBuffer,\r
486     uint32_t    ulBlockNew)\r
487 {\r
488     uint8_t     bIdx;\r
489 \r
490     if(    !BufferToIdx(pBuffer, &bIdx)\r
491         || (ulBlockNew >= gpRedVolume->ulBlockCount))\r
492     {\r
493         REDERROR();\r
494     }\r
495     else\r
496     {\r
497         BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
498 \r
499         REDASSERT(pHead->bRefCount > 0U);\r
500         REDASSERT((pHead->uFlags & BFLAG_DIRTY) == 0U);\r
501 \r
502         pHead->uFlags |= BFLAG_DIRTY;\r
503         pHead->ulBlock = ulBlockNew;\r
504     }\r
505 }\r
506 \r
507 \r
508 #if (REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED\r
509 /** @brief Discard a buffer, releasing it and marking it invalid.\r
510 \r
511     @param pBuffer  The buffer to discard.\r
512 */\r
513 void RedBufferDiscard(\r
514     const void *pBuffer)\r
515 {\r
516     uint8_t     bIdx;\r
517 \r
518     if(!BufferToIdx(pBuffer, &bIdx))\r
519     {\r
520         REDERROR();\r
521     }\r
522     else\r
523     {\r
524         REDASSERT(gBufCtx.aHead[bIdx].bRefCount == 1U);\r
525         REDASSERT(gBufCtx.uNumUsed > 0U);\r
526 \r
527         gBufCtx.aHead[bIdx].bRefCount = 0U;\r
528         gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;\r
529 \r
530         gBufCtx.uNumUsed--;\r
531 \r
532         BufferMakeLRU(bIdx);\r
533     }\r
534 }\r
535 #endif\r
536 #endif /* REDCONF_READ_ONLY == 0 */\r
537 \r
538 \r
539 /** @brief Discard a range of buffers, marking them invalid.\r
540 \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
544 \r
545     @return A negated ::REDSTATUS code indicating the operation result.\r
546 \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
550 */\r
551 REDSTATUS RedBufferDiscardRange(\r
552     uint32_t    ulBlockStart,\r
553     uint32_t    ulBlockCount)\r
554 {\r
555     REDSTATUS   ret = 0;\r
556 \r
557     if(    (ulBlockStart >= gpRedVolume->ulBlockCount)\r
558         || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)\r
559         || (ulBlockCount == 0U))\r
560     {\r
561         REDERROR();\r
562         ret = -RED_EINVAL;\r
563     }\r
564     else\r
565     {\r
566         uint8_t bIdx;\r
567 \r
568         for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
569         {\r
570             BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
571 \r
572             if(    (pHead->bVolNum == gbRedVolNum)\r
573                 && (pHead->ulBlock != BBLK_INVALID)\r
574                 && (pHead->ulBlock >= ulBlockStart)\r
575                 && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))\r
576             {\r
577                 if(pHead->bRefCount == 0U)\r
578                 {\r
579                     pHead->ulBlock = BBLK_INVALID;\r
580 \r
581                     BufferMakeLRU(bIdx);\r
582                 }\r
583                 else\r
584                 {\r
585                     /*  This should never happen.  There are three general cases\r
586                         when this function is used:\r
587 \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
593                            beforehand.\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
598                     */\r
599                     CRITICAL_ERROR();\r
600                     ret = -RED_EBUSY;\r
601                     break;\r
602                 }\r
603             }\r
604         }\r
605     }\r
606 \r
607     return ret;\r
608 }\r
609 \r
610 \r
611 /** Determine whether a metadata buffer is valid.\r
612 \r
613     This includes checking its signature, CRC, and sequence number.\r
614 \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
618 \r
619     @return Whether the metadata buffer is valid.\r
620 \r
621     @retval true    The metadata buffer is valid.\r
622     @retval false   The metadata buffer is invalid.\r
623 */\r
624 static bool BufferIsValid(\r
625     const uint8_t  *pbBuffer,\r
626     uint16_t        uFlags)\r
627 {\r
628     bool            fValid;\r
629 \r
630     if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
631     {\r
632         REDERROR();\r
633         fValid = false;\r
634     }\r
635     else\r
636     {\r
637         NODEHEADER  buf;\r
638         uint16_t    uMetaFlags = uFlags & BFLAG_META_MASK;\r
639 \r
640         /*  Casting pbBuffer to (NODEHEADER *) would run afoul MISRA-C:2012\r
641             R11.3, so instead copy the fields out.\r
642         */\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
646 \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
651       #endif\r
652 \r
653         /*  Make sure the signature is correct for the type of metadata block\r
654             requested by the caller.\r
655         */\r
656         switch(buf.ulSignature)\r
657         {\r
658             case META_SIG_MASTER:\r
659                 fValid = (uMetaFlags == BFLAG_META_MASTER);\r
660                 break;\r
661           #if REDCONF_IMAP_EXTERNAL == 1\r
662             case META_SIG_IMAP:\r
663                 fValid = (uMetaFlags == BFLAG_META_IMAP);\r
664                 break;\r
665           #endif\r
666             case META_SIG_INODE:\r
667                 fValid = (uMetaFlags == BFLAG_META_INODE);\r
668                 break;\r
669           #if DINDIR_POINTERS > 0U\r
670             case META_SIG_DINDIR:\r
671                 fValid = (uMetaFlags == BFLAG_META_DINDIR);\r
672                 break;\r
673           #endif\r
674           #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
675             case META_SIG_INDIR:\r
676                 fValid = (uMetaFlags == BFLAG_META_INDIR);\r
677                 break;\r
678           #endif\r
679             default:\r
680                 fValid = false;\r
681                 break;\r
682         }\r
683 \r
684         if(fValid)\r
685         {\r
686             uint32_t ulComputedCrc;\r
687 \r
688             /*  Check for disk corruption by comparing the stored CRC with one\r
689                 computed from the data.\r
690 \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
696             */\r
697             ulComputedCrc = RedCrcNode(pbBuffer);\r
698             if(buf.ulCRC != ulComputedCrc)\r
699             {\r
700                 fValid = false;\r
701             }\r
702             else if(gpRedVolume->fMounted && (buf.ullSequence >= gpRedVolume->ullSequence))\r
703             {\r
704                 fValid = false;\r
705             }\r
706             else\r
707             {\r
708                 /*  Buffer is valid.  No action, fValid is already true.\r
709                 */\r
710             }\r
711         }\r
712     }\r
713 \r
714     return fValid;\r
715 }\r
716 \r
717 \r
718 /** @brief Derive the index of the buffer.\r
719 \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
722 \r
723     @return Boolean indicating result.\r
724 \r
725     @retval true    Success.\r
726     @retval false   Failure.  @p pBuffer is not a valid buffer pointer.\r
727 */\r
728 static bool BufferToIdx(\r
729     const void *pBuffer,\r
730     uint8_t    *pbIdx)\r
731 {\r
732     bool        fRet = false;\r
733 \r
734     if((pBuffer != NULL) && (pbIdx != NULL))\r
735     {\r
736         uint8_t bIdx;\r
737 \r
738         /*  pBuffer should be a pointer to one of the block buffers.\r
739 \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
745         */\r
746         for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
747         {\r
748             if(pBuffer == &gBufCtx.b.aabBuffer[bIdx][0U])\r
749             {\r
750                 break;\r
751             }\r
752         }\r
753 \r
754         if(    (bIdx < REDCONF_BUFFER_COUNT)\r
755             && (gBufCtx.aHead[bIdx].ulBlock != BBLK_INVALID)\r
756             && (gBufCtx.aHead[bIdx].bVolNum == gbRedVolNum))\r
757         {\r
758             *pbIdx = bIdx;\r
759             fRet = true;\r
760         }\r
761     }\r
762 \r
763     return fRet;\r
764 }\r
765 \r
766 \r
767 #if REDCONF_READ_ONLY == 0\r
768 /** @brief Write out a dirty buffer.\r
769 \r
770     @param bIdx The index of the buffer to write.\r
771 \r
772     @return A negated ::REDSTATUS code indicating the operation result.\r
773 \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
777 */\r
778 static REDSTATUS BufferWrite(\r
779     uint8_t     bIdx)\r
780 {\r
781     REDSTATUS   ret = 0;\r
782 \r
783     if(bIdx < REDCONF_BUFFER_COUNT)\r
784     {\r
785         const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
786 \r
787         REDASSERT((pHead->uFlags & BFLAG_DIRTY) != 0U);\r
788 \r
789         if((pHead->uFlags & BFLAG_META) != 0U)\r
790         {\r
791             ret = BufferFinalize(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);\r
792         }\r
793 \r
794         if(ret == 0)\r
795         {\r
796             ret = RedIoWrite(pHead->bVolNum, pHead->ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);\r
797 \r
798           #ifdef REDCONF_ENDIAN_SWAP\r
799             BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);\r
800           #endif\r
801         }\r
802     }\r
803     else\r
804     {\r
805         REDERROR();\r
806         ret = -RED_EINVAL;\r
807     }\r
808 \r
809     return ret;\r
810 }\r
811 \r
812 \r
813 /** @brief Finalize a metadata buffer.\r
814 \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
817 \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
820                     signature.\r
821 \r
822     @return A negated ::REDSTATUS code indicating the operation result.\r
823 \r
824     @retval 0           Operation was successful.\r
825     @retval -RED_EINVAL Invalid parameter; or maximum sequence number reached.\r
826 */\r
827 static REDSTATUS BufferFinalize(\r
828     uint8_t    *pbBuffer,\r
829     uint16_t    uFlags)\r
830 {\r
831     REDSTATUS   ret = 0;\r
832 \r
833     if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
834     {\r
835         REDERROR();\r
836         ret = -RED_EINVAL;\r
837     }\r
838     else\r
839     {\r
840         uint32_t ulSignature;\r
841 \r
842         switch(uFlags & BFLAG_META_MASK)\r
843         {\r
844             case BFLAG_META_MASTER:\r
845                 ulSignature = META_SIG_MASTER;\r
846                 break;\r
847           #if REDCONF_IMAP_EXTERNAL == 1\r
848             case BFLAG_META_IMAP:\r
849                 ulSignature = META_SIG_IMAP;\r
850                 break;\r
851           #endif\r
852             case BFLAG_META_INODE:\r
853                 ulSignature = META_SIG_INODE;\r
854                 break;\r
855           #if DINDIR_POINTERS > 0U\r
856             case BFLAG_META_DINDIR:\r
857                 ulSignature = META_SIG_DINDIR;\r
858                 break;\r
859           #endif\r
860           #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
861             case BFLAG_META_INDIR:\r
862                 ulSignature = META_SIG_INDIR;\r
863                 break;\r
864           #endif\r
865             default:\r
866                 ulSignature = 0U;\r
867                 break;\r
868         }\r
869 \r
870         if(ulSignature == 0U)\r
871         {\r
872             REDERROR();\r
873             ret = -RED_EINVAL;\r
874         }\r
875         else\r
876         {\r
877             uint64_t ullSeqNum = gpRedVolume->ullSequence;\r
878 \r
879             ret = RedVolSeqNumIncrement();\r
880             if(ret == 0)\r
881             {\r
882                 uint32_t ulCrc;\r
883 \r
884                 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SIG], &ulSignature, sizeof(ulSignature));\r
885                 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SEQ], &ullSeqNum, sizeof(ullSeqNum));\r
886 \r
887               #ifdef REDCONF_ENDIAN_SWAP\r
888                 BufferEndianSwap(pbBuffer, uFlags);\r
889               #endif\r
890 \r
891                 ulCrc = RedCrcNode(pbBuffer);\r
892               #ifdef REDCONF_ENDIAN_SWAP\r
893                 ulCrc = RedRev32(ulCrc);\r
894               #endif\r
895                 RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_CRC], &ulCrc, sizeof(ulCrc));\r
896             }\r
897         }\r
898     }\r
899 \r
900     return ret;\r
901 }\r
902 #endif /* REDCONF_READ_ONLY == 0 */\r
903 \r
904 \r
905 #ifdef REDCONF_ENDIAN_SWAP\r
906 /** @brief Swap the byte order of a metadata buffer\r
907 \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
910 \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
913                     metadata node.\r
914 */\r
915 static void BufferEndianSwap(\r
916     void       *pBuffer,\r
917     uint16_t    uFlags)\r
918 {\r
919     if((pBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
920     {\r
921         REDERROR();\r
922     }\r
923     else if((uFlags & BFLAG_META_MASK) != 0)\r
924     {\r
925         BufferEndianSwapHeader(pBuffer);\r
926 \r
927         switch(uFlags & BFLAG_META_MASK)\r
928         {\r
929             case BFLAG_META_MASTER:\r
930                 BufferEndianSwapMaster(pBuffer);\r
931                 break;\r
932             case BFLAG_META_INODE:\r
933                 BufferEndianSwapInode(pBuffer);\r
934                 break;\r
935           #if DINDIR_POINTERS > 0U\r
936             case BFLAG_META_DINDIR:\r
937                 BufferEndianSwapIndir(pBuffer);\r
938                 break;\r
939           #endif\r
940           #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
941             case BFLAG_META_INDIR:\r
942                 BufferEndianSwapIndir(pBuffer);\r
943                 break;\r
944           #endif\r
945             default:\r
946                 break;\r
947         }\r
948     }\r
949     else\r
950     {\r
951         /*  File data buffers do not need to be swapped.\r
952         */\r
953     }\r
954 }\r
955 \r
956 \r
957 /** @brief Swap the byte order of a metadata node header\r
958 \r
959     @param pHeader  Pointer to the metadata node header to swap\r
960 */\r
961 static void BufferEndianSwapHeader(\r
962     NODEHEADER *pHeader)\r
963 {\r
964     if(pHeader == NULL)\r
965     {\r
966         REDERROR();\r
967     }\r
968     else\r
969     {\r
970         pHeader->ulSignature = RedRev32(pHeader->ulSignature);\r
971         pHeader->ulCRC = RedRev32(pHeader->ulCRC);\r
972         pHeader->ullSequence = RedRev64(pHeader->ullSequence);\r
973     }\r
974 }\r
975 \r
976 \r
977 /** @brief Swap the byte order of a master block\r
978 \r
979     @param pMaster  Pointer to the master block to swap\r
980 */\r
981 static void BufferEndianSwapMaster(\r
982     MASTERBLOCK *pMaster)\r
983 {\r
984     if(pMaster == NULL)\r
985     {\r
986         REDERROR();\r
987     }\r
988     else\r
989     {\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
997     }\r
998 }\r
999 \r
1000 \r
1001 /** @brief Swap the byte order of an inode\r
1002 \r
1003     @param pInode   Pointer to the inode to swap\r
1004 */\r
1005 static void BufferEndianSwapInode(\r
1006     INODE  *pInode)\r
1007 {\r
1008     if(pInode == NULL)\r
1009     {\r
1010         REDERROR();\r
1011     }\r
1012     else\r
1013     {\r
1014         uint32_t ulIdx;\r
1015 \r
1016         pInode->ullSize = RedRev64(pInode->ullSize);\r
1017 \r
1018       #if REDCONF_INODE_BLOCKS == 1\r
1019         pInode->ulBlocks = RedRev32(pInode->ulBlocks);\r
1020       #endif\r
1021 \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
1026       #endif\r
1027 \r
1028         pInode->uMode = RedRev16(pInode->uMode);\r
1029 \r
1030       #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)\r
1031         pInode->uNLink = RedRev16(pInode->uNLink);\r
1032       #endif\r
1033 \r
1034       #if REDCONF_API_POSIX == 1\r
1035         pInode->ulPInode = RedRev32(pInode->ulPInode);\r
1036       #endif\r
1037 \r
1038         for(ulIdx = 0; ulIdx < INODE_ENTRIES; ulIdx++)\r
1039         {\r
1040             pInode->aulEntries[ulIdx] = RedRev32(pInode->aulEntries[ulIdx]);\r
1041         }\r
1042     }\r
1043 }\r
1044 \r
1045 \r
1046 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
1047 /** @brief Swap the byte order of an indirect or double indirect node\r
1048 \r
1049     @param pIndir   Pointer to the node to swap\r
1050 */\r
1051 static void BufferEndianSwapIndir(\r
1052     INDIR  *pIndir)\r
1053 {\r
1054     if(pIndir == NULL)\r
1055     {\r
1056         REDERROR();\r
1057     }\r
1058     else\r
1059     {\r
1060         uint32_t ulIdx;\r
1061 \r
1062         pIndir->ulInode = RedRev32(pIndir->ulInode);\r
1063 \r
1064         for(ulIdx = 0; ulIdx < INDIR_ENTRIES; ulIdx++)\r
1065         {\r
1066             pIndir->aulEntries[ulIdx] = RedRev32(pIndir->aulEntries[ulIdx]);\r
1067         }\r
1068     }\r
1069 }\r
1070 \r
1071 #endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */\r
1072 #endif /* #ifdef REDCONF_ENDIAN_SWAP */\r
1073 \r
1074 \r
1075 /** @brief Mark a buffer as least recently used.\r
1076 \r
1077     @param bIdx The index of the buffer to make LRU.\r
1078 */\r
1079 static void BufferMakeLRU(\r
1080     uint8_t bIdx)\r
1081 {\r
1082     if(bIdx >= REDCONF_BUFFER_COUNT)\r
1083     {\r
1084         REDERROR();\r
1085     }\r
1086     else if(bIdx != gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U])\r
1087     {\r
1088         uint8_t bMruIdx;\r
1089 \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
1093         */\r
1094         for(bMruIdx = 0U; bMruIdx < (REDCONF_BUFFER_COUNT - 1U); bMruIdx++)\r
1095         {\r
1096             if(bIdx == gBufCtx.abMRU[bMruIdx])\r
1097             {\r
1098                 break;\r
1099             }\r
1100         }\r
1101 \r
1102         if(bMruIdx < (REDCONF_BUFFER_COUNT - 1U))\r
1103         {\r
1104             /*  Move the buffer index to the back of the MRU array, making it\r
1105                 the LRU buffer.\r
1106             */\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
1109         }\r
1110         else\r
1111         {\r
1112             REDERROR();\r
1113         }\r
1114     }\r
1115     else\r
1116     {\r
1117         /*  Buffer already LRU, nothing to do.\r
1118         */\r
1119     }\r
1120 }\r
1121 \r
1122 \r
1123 /** @brief Mark a buffer as most recently used.\r
1124 \r
1125     @param bIdx The index of the buffer to make MRU.\r
1126 */\r
1127 static void BufferMakeMRU(\r
1128     uint8_t bIdx)\r
1129 {\r
1130     if(bIdx >= REDCONF_BUFFER_COUNT)\r
1131     {\r
1132         REDERROR();\r
1133     }\r
1134     else if(bIdx != gBufCtx.abMRU[0U])\r
1135     {\r
1136         uint8_t bMruIdx;\r
1137 \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
1141         */\r
1142         for(bMruIdx = 1U; bMruIdx < REDCONF_BUFFER_COUNT; bMruIdx++)\r
1143         {\r
1144             if(bIdx == gBufCtx.abMRU[bMruIdx])\r
1145             {\r
1146                 break;\r
1147             }\r
1148         }\r
1149 \r
1150         if(bMruIdx < REDCONF_BUFFER_COUNT)\r
1151         {\r
1152             /*  Move the buffer index to the front of the MRU array, making it\r
1153                 the MRU buffer.\r
1154             */\r
1155             RedMemMove(&gBufCtx.abMRU[1U], &gBufCtx.abMRU[0U], bMruIdx);\r
1156             gBufCtx.abMRU[0U] = bIdx;\r
1157         }\r
1158         else\r
1159         {\r
1160             REDERROR();\r
1161         }\r
1162     }\r
1163     else\r
1164     {\r
1165         /*  Buffer already MRU, nothing to do.\r
1166         */\r
1167     }\r
1168 }\r
1169 \r
1170 \r
1171 /** @brief Find a block in the buffers.\r
1172 \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
1176 \r
1177     @return Boolean indicating whether or not the block is buffered.\r
1178 \r
1179     @retval true    @p ulBlock is buffered, and its index has been stored in\r
1180                     @p pbIdx.\r
1181     @retval false   @p ulBlock is not buffered.\r
1182 */\r
1183 static bool BufferFind(\r
1184     uint32_t ulBlock,\r
1185     uint8_t *pbIdx)\r
1186 {\r
1187     bool     ret = false;\r
1188 \r
1189     if((ulBlock >= gpRedVolume->ulBlockCount) || (pbIdx == NULL))\r
1190     {\r
1191         REDERROR();\r
1192     }\r
1193     else\r
1194     {\r
1195         uint8_t bIdx;\r
1196 \r
1197         for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
1198         {\r
1199             const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
1200 \r
1201             if((pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock == ulBlock))\r
1202             {\r
1203                 *pbIdx = bIdx;\r
1204                 ret = true;\r
1205                 break;\r
1206             }\r
1207         }\r
1208     }\r
1209 \r
1210     return ret;\r
1211 }\r
1212 \r