]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/core/driver/volume.c
Update Reliance Edge fail safe file system to the latest version.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / core / driver / volume.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 core volume operations.\r
27 */\r
28 #include <redfs.h>\r
29 #include <redcore.h>\r
30 \r
31 \r
32 static bool MetarootIsValid(METAROOT *pMR, bool *pfSectorCRCIsValid);\r
33 #ifdef REDCONF_ENDIAN_SWAP\r
34 static void MetaRootEndianSwap(METAROOT *pMetaRoot);\r
35 #endif\r
36 \r
37 \r
38 /** @brief Mount a file system volume.\r
39 \r
40     @return A negated ::REDSTATUS code indicating the operation result.\r
41 \r
42     @retval 0           Operation was successful.\r
43     @retval -RED_EIO    Volume not formatted, improperly formatted, or corrupt.\r
44 */\r
45 REDSTATUS RedVolMount(void)\r
46 {\r
47     REDSTATUS ret;\r
48 \r
49   #if REDCONF_READ_ONLY == 0\r
50     ret = RedOsBDevOpen(gbRedVolNum, BDEV_O_RDWR);\r
51   #else\r
52     ret = RedOsBDevOpen(gbRedVolNum, BDEV_O_RDONLY);\r
53   #endif\r
54 \r
55     if(ret == 0)\r
56     {\r
57         ret = RedVolMountMaster();\r
58 \r
59         if(ret == 0)\r
60         {\r
61             ret = RedVolMountMetaroot();\r
62         }\r
63 \r
64         if(ret != 0)\r
65         {\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
68             */\r
69             (void)RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount);\r
70             (void)RedOsBDevClose(gbRedVolNum);\r
71         }\r
72     }\r
73 \r
74     return ret;\r
75 }\r
76 \r
77 \r
78 /** @brief Mount the master block.\r
79 \r
80     @return A negated ::REDSTATUS code indicating the operation result.\r
81 \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
85 */\r
86 REDSTATUS RedVolMountMaster(void)\r
87 {\r
88     REDSTATUS       ret;\r
89     MASTERBLOCK    *pMB;\r
90 \r
91     /*  Read the master block, to ensure that the disk was formatted with\r
92         Reliance Edge.\r
93     */\r
94     ret = RedBufferGet(BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR(&pMB));\r
95 \r
96     if(ret == 0)\r
97     {\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
101             to be reformatted.\r
102         */\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
113         {\r
114             ret = -RED_EIO;\r
115         }\r
116       #if REDCONF_API_POSIX == 1\r
117         else if(((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U) != (REDCONF_API_POSIX_LINK == 1))\r
118         {\r
119             ret = -RED_EIO;\r
120         }\r
121       #else\r
122         else if((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U)\r
123         {\r
124             ret = -RED_EIO;\r
125         }\r
126       #endif\r
127         else\r
128         {\r
129             /*  Master block configuration is valid.\r
130 \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
134             */\r
135             gpRedVolume->ullSequence = pMB->hdr.ullSequence;\r
136         }\r
137 \r
138         RedBufferPut(pMB);\r
139     }\r
140 \r
141     return ret;\r
142 }\r
143 \r
144 \r
145 /** @brief Mount the latest metaroot.\r
146 \r
147     This function also populates the volume contexts.\r
148 \r
149     @return A negated ::REDSTATUS code indicating the operation result.\r
150 \r
151     @retval 0           Operation was successful.\r
152     @retval -RED_EIO    Both metaroots are missing or corrupt.\r
153 */\r
154 REDSTATUS RedVolMountMetaroot(void)\r
155 {\r
156     REDSTATUS ret;\r
157 \r
158     ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[0U]);\r
159 \r
160     if(ret == 0)\r
161     {\r
162         ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[1U]);\r
163     }\r
164 \r
165     /*  Determine which metaroot is the most recent copy that was written\r
166         completely.\r
167     */\r
168     if(ret == 0)\r
169     {\r
170         uint8_t bMR = UINT8_MAX;\r
171         bool    fSectorCRCIsValid;\r
172 \r
173         if(MetarootIsValid(&gpRedCoreVol->aMR[0U], &fSectorCRCIsValid))\r
174         {\r
175             bMR = 0U;\r
176 \r
177           #ifdef REDCONF_ENDIAN_SWAP\r
178             MetaRootEndianSwap(&gpRedCoreVol->aMR[0U]);\r
179           #endif\r
180         }\r
181         else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)\r
182         {\r
183             ret = -RED_EIO;\r
184         }\r
185         else\r
186         {\r
187             /*  Metaroot is not valid, so it is ignored and there's nothing\r
188                 to do here.\r
189             */\r
190         }\r
191 \r
192         if(ret == 0)\r
193         {\r
194             if(MetarootIsValid(&gpRedCoreVol->aMR[1U], &fSectorCRCIsValid))\r
195             {\r
196               #ifdef REDCONF_ENDIAN_SWAP\r
197                 MetaRootEndianSwap(&gpRedCoreVol->aMR[1U]);\r
198               #endif\r
199 \r
200                 if((bMR != 0U) || (gpRedCoreVol->aMR[1U].hdr.ullSequence > gpRedCoreVol->aMR[0U].hdr.ullSequence))\r
201                 {\r
202                     bMR = 1U;\r
203                 }\r
204             }\r
205             else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid)\r
206             {\r
207                 ret = -RED_EIO;\r
208             }\r
209             else\r
210             {\r
211                 /*  Metaroot is not valid, so it is ignored and there's nothing\r
212                     to do here.\r
213                 */\r
214             }\r
215         }\r
216 \r
217         if(ret == 0)\r
218         {\r
219             if(bMR == UINT8_MAX)\r
220             {\r
221                 /*  Neither metaroot was valid.\r
222                 */\r
223                 ret = -RED_EIO;\r
224             }\r
225             else\r
226             {\r
227                 gpRedCoreVol->bCurMR = bMR;\r
228                 gpRedMR = &gpRedCoreVol->aMR[bMR];\r
229             }\r
230         }\r
231     }\r
232 \r
233     if(ret == 0)\r
234     {\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
239         */\r
240         if(gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence)\r
241         {\r
242             gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence;\r
243         }\r
244 \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
248         */\r
249         ret = RedVolSeqNumIncrement();\r
250     }\r
251 \r
252     if(ret == 0)\r
253     {\r
254         gpRedVolume->fMounted = true;\r
255       #if REDCONF_READ_ONLY == 0\r
256         gpRedVolume->fReadOnly = false;\r
257       #endif\r
258 \r
259       #if RESERVED_BLOCKS > 0U\r
260         gpRedCoreVol->fUseReservedBlocks = false;\r
261       #endif\r
262         gpRedCoreVol->ulAlmostFreeBlocks = 0U;\r
263 \r
264         gpRedCoreVol->aMR[1U - gpRedCoreVol->bCurMR] = *gpRedMR;\r
265         gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR;\r
266         gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];\r
267     }\r
268 \r
269     return ret;\r
270 }\r
271 \r
272 \r
273 /** @brief Determine whether the metaroot is valid.\r
274 \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
278 \r
279     @return Whether the metaroot is valid.\r
280 \r
281     @retval true    The metaroot buffer is valid.\r
282     @retval false   The metaroot buffer is invalid.\r
283 */\r
284 static bool MetarootIsValid(\r
285     METAROOT   *pMR,\r
286     bool       *pfSectorCRCIsValid)\r
287 {\r
288     bool        fRet = false;\r
289 \r
290     if(pfSectorCRCIsValid == NULL)\r
291     {\r
292         REDERROR();\r
293     }\r
294     else if(pMR == NULL)\r
295     {\r
296         REDERROR();\r
297         *pfSectorCRCIsValid = false;\r
298     }\r
299   #ifdef REDCONF_ENDIAN_SWAP\r
300     else if(RedRev32(pMR->hdr.ulSignature) != META_SIG_METAROOT)\r
301   #else\r
302     else if(pMR->hdr.ulSignature != META_SIG_METAROOT)\r
303   #endif\r
304     {\r
305         *pfSectorCRCIsValid = false;\r
306     }\r
307     else\r
308     {\r
309         const uint8_t  *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMR);\r
310         uint32_t        ulSectorCRC = pMR->ulSectorCRC;\r
311         uint32_t        ulCRC;\r
312 \r
313       #ifdef REDCONF_ENDIAN_SWAP\r
314         ulSectorCRC = RedRev32(ulSectorCRC);\r
315       #endif\r
316 \r
317         /*  The sector CRC was zero when the CRC was computed during the\r
318             transaction, so it must be zero here.\r
319         */\r
320         pMR->ulSectorCRC = 0U;\r
321 \r
322         ulCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);\r
323 \r
324         fRet = ulCRC == ulSectorCRC;\r
325         *pfSectorCRCIsValid = fRet;\r
326 \r
327         if(fRet)\r
328         {\r
329             if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)\r
330             {\r
331                 ulCRC = RedCrc32Update(ulCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);\r
332             }\r
333 \r
334           #ifdef REDCONF_ENDIAN_SWAP\r
335             ulCRC = RedRev32(ulCRC);\r
336           #endif\r
337 \r
338             fRet = ulCRC == pMR->hdr.ulCRC;\r
339         }\r
340     }\r
341 \r
342     return fRet;\r
343 }\r
344 \r
345 \r
346 #if REDCONF_READ_ONLY == 0\r
347 /** @brief Commit a transaction point.\r
348 \r
349     @return A negated ::REDSTATUS code indicating the operation result.\r
350 \r
351     @retval 0           Operation was successful.\r
352     @retval -RED_EIO    A disk I/O error occurred.\r
353 */\r
354 REDSTATUS RedVolTransact(void)\r
355 {\r
356     REDSTATUS ret = 0;\r
357 \r
358     REDASSERT(!gpRedVolume->fReadOnly); /* Should be checked by caller. */\r
359 \r
360     if(gpRedCoreVol->fBranched)\r
361     {\r
362         gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks;\r
363         gpRedCoreVol->ulAlmostFreeBlocks = 0U;\r
364 \r
365         ret = RedBufferFlush(0U, gpRedVolume->ulBlockCount);\r
366 \r
367         if(ret == 0)\r
368         {\r
369             gpRedMR->hdr.ulSignature = META_SIG_METAROOT;\r
370             gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence;\r
371 \r
372             ret = RedVolSeqNumIncrement();\r
373         }\r
374 \r
375         if(ret == 0)\r
376         {\r
377             const uint8_t  *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(gpRedMR);\r
378             uint32_t        ulSectorCRC;\r
379 \r
380           #ifdef REDCONF_ENDIAN_SWAP\r
381             MetaRootEndianSwap(gpRedMR);\r
382           #endif\r
383 \r
384             gpRedMR->ulSectorCRC = 0U;\r
385 \r
386             ulSectorCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U);\r
387 \r
388             if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE)\r
389             {\r
390                 gpRedMR->hdr.ulCRC = RedCrc32Update(ulSectorCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize);\r
391             }\r
392             else\r
393             {\r
394                 gpRedMR->hdr.ulCRC = ulSectorCRC;\r
395             }\r
396 \r
397             gpRedMR->ulSectorCRC = ulSectorCRC;\r
398 \r
399           #ifdef REDCONF_ENDIAN_SWAP\r
400             gpRedMR->hdr.ulCRC = RedRev32(gpRedMR->hdr.ulCRC);\r
401             gpRedMR->ulSectorCRC = RedRev32(gpRedMR->ulSectorCRC);\r
402           #endif\r
403 \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
409             */\r
410             ret = RedIoFlush(gbRedVolNum);\r
411         }\r
412 \r
413         if(ret == 0)\r
414         {\r
415             ret = RedIoWrite(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR);\r
416 \r
417           #ifdef REDCONF_ENDIAN_SWAP\r
418             MetaRootEndianSwap(gpRedMR);\r
419           #endif\r
420         }\r
421 \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
424         */\r
425         if(ret == 0)\r
426         {\r
427             ret = RedIoFlush(gbRedVolNum);\r
428         }\r
429 \r
430         /*  Toggle to the other metaroot buffer.  The working state and committed\r
431             state metaroot buffers exchange places.\r
432         */\r
433         if(ret == 0)\r
434         {\r
435             uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR;\r
436 \r
437             gpRedCoreVol->aMR[bNextMR] = *gpRedMR;\r
438             gpRedCoreVol->bCurMR = bNextMR;\r
439 \r
440             gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR];\r
441 \r
442             gpRedCoreVol->fBranched = false;\r
443         }\r
444 \r
445         CRITICAL_ASSERT(ret == 0);\r
446     }\r
447 \r
448     return ret;\r
449 }\r
450 #endif\r
451 \r
452 \r
453 #ifdef REDCONF_ENDIAN_SWAP\r
454 static void MetaRootEndianSwap(\r
455     METAROOT *pMetaRoot)\r
456 {\r
457     if(pMetaRoot == NULL)\r
458     {\r
459         REDERROR();\r
460     }\r
461     else\r
462     {\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
467       #endif\r
468         pMetaRoot->ulAllocNextBlock = RedRev32(pMetaRoot->ulAllocNextBlock);\r
469     }\r
470 }\r
471 #endif\r
472 \r
473 \r
474 /** @brief Process a critical file system error.\r
475 \r
476     @param pszFileName  The file in which the error occurred.\r
477     @param ulLineNum    The line number at which the error occurred.\r
478 */\r
479 void RedVolCriticalError(\r
480     const char *pszFileName,\r
481     uint32_t    ulLineNum)\r
482 {\r
483   #if REDCONF_OUTPUT == 1\r
484   #if REDCONF_READ_ONLY == 0\r
485     if(!gpRedVolume->fReadOnly)\r
486     {\r
487         RedOsOutputString("Critical file system error in Reliance Edge, setting volume to READONLY\n");\r
488     }\r
489     else\r
490   #endif\r
491     {\r
492         RedOsOutputString("Critical file system error in Reliance Edge (volume already READONLY)\n");\r
493     }\r
494   #endif\r
495 \r
496   #if REDCONF_READ_ONLY == 0\r
497     gpRedVolume->fReadOnly = true;\r
498   #endif\r
499 \r
500   #if REDCONF_ASSERTS == 1\r
501     RedOsAssertFail(pszFileName, ulLineNum);\r
502   #else\r
503     (void)pszFileName;\r
504     (void)ulLineNum;\r
505   #endif\r
506 }\r
507 \r
508 \r
509 /** @brief Increment the sequence number.\r
510 \r
511     @return A negated ::REDSTATUS code indicating the operation result.\r
512 \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
516 */\r
517 REDSTATUS RedVolSeqNumIncrement(void)\r
518 {\r
519     REDSTATUS ret;\r
520 \r
521     if(gpRedVolume->ullSequence == UINT64_MAX)\r
522     {\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
528         */\r
529         CRITICAL_ERROR();\r
530         ret = -RED_EFUBAR;\r
531     }\r
532     else\r
533     {\r
534         gpRedVolume->ullSequence++;\r
535         ret = 0;\r
536     }\r
537 \r
538     return ret;\r
539 }\r
540 \r