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