1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2  *
3  *                 Copyright (c) 2014-2015 Datalight, Inc.
4  *                     All Rights Reserved Worldwide.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; use version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 /*  Businesses and individuals that for commercial or other reasons cannot
21  *  comply with the terms of the GPLv2 license may obtain a commercial license
22  *  before incorporating Reliance Edge into proprietary software for
23  *  distribution in any form.  Visit http://www.datalight.com/reliance-edge for
24  *  more information.
25  */
26 
27 /** @file
28  *  @brief Implements core volume operations.
29  */
30 #include <redfs.h>
31 #include <redcore.h>
32 
33 
34 static bool MetarootIsValid( METAROOT * pMR,
35                              bool * pfSectorCRCIsValid );
36 #ifdef REDCONF_ENDIAN_SWAP
37     static void MetaRootEndianSwap( METAROOT * pMetaRoot );
38 #endif
39 
40 
41 /** @brief Mount a file system volume.
42  *
43  *  @return A negated ::REDSTATUS code indicating the operation result.
44  *
45  *  @retval 0           Operation was successful.
46  *  @retval -RED_EIO    Volume not formatted, improperly formatted, or corrupt.
47  */
RedVolMount(void)48 REDSTATUS RedVolMount( void )
49 {
50     REDSTATUS ret;
51 
52     #if REDCONF_READ_ONLY == 0
53         ret = RedOsBDevOpen( gbRedVolNum, BDEV_O_RDWR );
54     #else
55         ret = RedOsBDevOpen( gbRedVolNum, BDEV_O_RDONLY );
56     #endif
57 
58     if( ret == 0 )
59     {
60         ret = RedVolMountMaster();
61 
62         if( ret == 0 )
63         {
64             ret = RedVolMountMetaroot();
65         }
66 
67         if( ret != 0 )
68         {
69             /*  If we fail to mount, invalidate the buffers to prevent any
70              *  confusion that could be caused by stale or corrupt metadata.
71              */
72             ( void ) RedBufferDiscardRange( 0U, gpRedVolume->ulBlockCount );
73             ( void ) RedOsBDevClose( gbRedVolNum );
74         }
75     }
76 
77     return ret;
78 }
79 
80 
81 /** @brief Mount the master block.
82  *
83  *  @return A negated ::REDSTATUS code indicating the operation result.
84  *
85  *  @retval 0           Operation was successful.
86  *  @retval -RED_EIO    Master block missing, corrupt, or inconsistent with the
87  *                      compile-time driver settings.
88  */
RedVolMountMaster(void)89 REDSTATUS RedVolMountMaster( void )
90 {
91     REDSTATUS ret;
92     MASTERBLOCK * pMB;
93 
94     /*  Read the master block, to ensure that the disk was formatted with
95      *  Reliance Edge.
96      */
97     ret = RedBufferGet( BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR( &pMB ) );
98 
99     if( ret == 0 )
100     {
101         /*  Verify that the driver was compiled with the same settings that
102          *  the disk was formatted with.  If not, the user has made a
103          *  mistake: either the driver settings are wrong, or the disk needs
104          *  to be reformatted.
105          */
106         if( ( pMB->ulVersion != RED_DISK_LAYOUT_VERSION ) ||
107             ( pMB->ulInodeCount != gpRedVolConf->ulInodeCount ) ||
108             ( pMB->ulBlockCount != gpRedVolume->ulBlockCount ) ||
109             ( pMB->uMaxNameLen != REDCONF_NAME_MAX ) ||
110             ( pMB->uDirectPointers != REDCONF_DIRECT_POINTERS ) ||
111             ( pMB->uIndirectPointers != REDCONF_INDIRECT_POINTERS ) ||
112             ( pMB->bBlockSizeP2 != BLOCK_SIZE_P2 ) ||
113             ( ( ( pMB->bFlags & MBFLAG_API_POSIX ) != 0U ) != ( REDCONF_API_POSIX == 1 ) ) ||
114             ( ( ( pMB->bFlags & MBFLAG_INODE_TIMESTAMPS ) != 0U ) != ( REDCONF_INODE_TIMESTAMPS == 1 ) ) ||
115             ( ( ( pMB->bFlags & MBFLAG_INODE_BLOCKS ) != 0U ) != ( REDCONF_INODE_BLOCKS == 1 ) ) )
116         {
117             ret = -RED_EIO;
118         }
119 
120         #if REDCONF_API_POSIX == 1
121             else if( ( ( pMB->bFlags & MBFLAG_INODE_NLINK ) != 0U ) != ( REDCONF_API_POSIX_LINK == 1 ) )
122             {
123                 ret = -RED_EIO;
124             }
125         #else
126             else if( ( pMB->bFlags & MBFLAG_INODE_NLINK ) != 0U )
127             {
128                 ret = -RED_EIO;
129             }
130         #endif
131         else
132         {
133             /*  Master block configuration is valid.
134              *
135              *  Save the sequence number of the master block in the volume,
136              *  since we need it later (see RedVolMountMetaroot()) and we do
137              *  not want to re-buffer the master block.
138              */
139             gpRedVolume->ullSequence = pMB->hdr.ullSequence;
140         }
141 
142         RedBufferPut( pMB );
143     }
144 
145     return ret;
146 }
147 
148 
149 /** @brief Mount the latest metaroot.
150  *
151  *  This function also populates the volume contexts.
152  *
153  *  @return A negated ::REDSTATUS code indicating the operation result.
154  *
155  *  @retval 0           Operation was successful.
156  *  @retval -RED_EIO    Both metaroots are missing or corrupt.
157  */
RedVolMountMetaroot(void)158 REDSTATUS RedVolMountMetaroot( void )
159 {
160     REDSTATUS ret;
161 
162     ret = RedIoRead( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[ 0U ] );
163 
164     if( ret == 0 )
165     {
166         ret = RedIoRead( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[ 1U ] );
167     }
168 
169     /*  Determine which metaroot is the most recent copy that was written
170      *  completely.
171      */
172     if( ret == 0 )
173     {
174         uint8_t bMR = UINT8_MAX;
175         bool fSectorCRCIsValid;
176 
177         if( MetarootIsValid( &gpRedCoreVol->aMR[ 0U ], &fSectorCRCIsValid ) )
178         {
179             bMR = 0U;
180 
181             #ifdef REDCONF_ENDIAN_SWAP
182                 MetaRootEndianSwap( &gpRedCoreVol->aMR[ 0U ] );
183             #endif
184         }
185         else if( gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid )
186         {
187             ret = -RED_EIO;
188         }
189         else
190         {
191             /*  Metaroot is not valid, so it is ignored and there's nothing
192              *  to do here.
193              */
194         }
195 
196         if( ret == 0 )
197         {
198             if( MetarootIsValid( &gpRedCoreVol->aMR[ 1U ], &fSectorCRCIsValid ) )
199             {
200                 #ifdef REDCONF_ENDIAN_SWAP
201                     MetaRootEndianSwap( &gpRedCoreVol->aMR[ 1U ] );
202                 #endif
203 
204                 if( ( bMR != 0U ) || ( gpRedCoreVol->aMR[ 1U ].hdr.ullSequence > gpRedCoreVol->aMR[ 0U ].hdr.ullSequence ) )
205                 {
206                     bMR = 1U;
207                 }
208             }
209             else if( gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid )
210             {
211                 ret = -RED_EIO;
212             }
213             else
214             {
215                 /*  Metaroot is not valid, so it is ignored and there's nothing
216                  *  to do here.
217                  */
218             }
219         }
220 
221         if( ret == 0 )
222         {
223             if( bMR == UINT8_MAX )
224             {
225                 /*  Neither metaroot was valid.
226                  */
227                 ret = -RED_EIO;
228             }
229             else
230             {
231                 gpRedCoreVol->bCurMR = bMR;
232                 gpRedMR = &gpRedCoreVol->aMR[ bMR ];
233             }
234         }
235     }
236 
237     if( ret == 0 )
238     {
239         /*  Normally the metaroot contains the highest sequence number, but the
240          *  master block is the last block written during format, so on a
241          *  freshly formatted volume the master block sequence number (stored in
242          *  gpRedVolume->ullSequence) will be higher than that in the metaroot.
243          */
244         if( gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence )
245         {
246             gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence;
247         }
248 
249         /*  gpRedVolume->ullSequence stores the *next* sequence number; to avoid
250          *  giving the next node written to disk the same sequence number as the
251          *  metaroot, increment it here.
252          */
253         ret = RedVolSeqNumIncrement();
254     }
255 
256     if( ret == 0 )
257     {
258         gpRedVolume->fMounted = true;
259         #if REDCONF_READ_ONLY == 0
260             gpRedVolume->fReadOnly = false;
261         #endif
262 
263         #if RESERVED_BLOCKS > 0U
264             gpRedCoreVol->fUseReservedBlocks = false;
265         #endif
266         gpRedCoreVol->ulAlmostFreeBlocks = 0U;
267 
268         gpRedCoreVol->aMR[ 1U - gpRedCoreVol->bCurMR ] = *gpRedMR;
269         gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR;
270         gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
271     }
272 
273     return ret;
274 }
275 
276 
277 /** @brief Determine whether the metaroot is valid.
278  *
279  *  @param pMR                  The metaroot buffer.
280  *  @param pfSectorCRCIsValid   Populated with whether the first sector of the
281  *                              metaroot buffer is valid.
282  *
283  *  @return Whether the metaroot is valid.
284  *
285  *  @retval true    The metaroot buffer is valid.
286  *  @retval false   The metaroot buffer is invalid.
287  */
MetarootIsValid(METAROOT * pMR,bool * pfSectorCRCIsValid)288 static bool MetarootIsValid( METAROOT * pMR,
289                              bool * pfSectorCRCIsValid )
290 {
291     bool fRet = false;
292 
293     if( pfSectorCRCIsValid == NULL )
294     {
295         REDERROR();
296     }
297     else if( pMR == NULL )
298     {
299         REDERROR();
300         *pfSectorCRCIsValid = false;
301     }
302 
303     #ifdef REDCONF_ENDIAN_SWAP
304         else if( RedRev32( pMR->hdr.ulSignature ) != META_SIG_METAROOT )
305     #else
306         else if( pMR->hdr.ulSignature != META_SIG_METAROOT )
307     #endif
308     {
309         *pfSectorCRCIsValid = false;
310     }
311     else
312     {
313         const uint8_t * pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pMR );
314         uint32_t ulSectorCRC = pMR->ulSectorCRC;
315         uint32_t ulCRC;
316 
317         #ifdef REDCONF_ENDIAN_SWAP
318             ulSectorCRC = RedRev32( ulSectorCRC );
319         #endif
320 
321         /*  The sector CRC was zero when the CRC was computed during the
322          *  transaction, so it must be zero here.
323          */
324         pMR->ulSectorCRC = 0U;
325 
326         ulCRC = RedCrc32Update( 0U, &pbMR[ 8U ], gpRedVolConf->ulSectorSize - 8U );
327 
328         fRet = ulCRC == ulSectorCRC;
329         *pfSectorCRCIsValid = fRet;
330 
331         if( fRet )
332         {
333             if( gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE )
334             {
335                 ulCRC = RedCrc32Update( ulCRC, &pbMR[ gpRedVolConf->ulSectorSize ], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize );
336             }
337 
338             #ifdef REDCONF_ENDIAN_SWAP
339                 ulCRC = RedRev32( ulCRC );
340             #endif
341 
342             fRet = ulCRC == pMR->hdr.ulCRC;
343         }
344     }
345 
346     return fRet;
347 }
348 
349 
350 #if REDCONF_READ_ONLY == 0
351 
352 /** @brief Commit a transaction point.
353  *
354  *  @return A negated ::REDSTATUS code indicating the operation result.
355  *
356  *  @retval 0           Operation was successful.
357  *  @retval -RED_EIO    A disk I/O error occurred.
358  */
RedVolTransact(void)359     REDSTATUS RedVolTransact( void )
360     {
361         REDSTATUS ret = 0;
362 
363         REDASSERT( !gpRedVolume->fReadOnly ); /* Should be checked by caller. */
364 
365         if( gpRedCoreVol->fBranched )
366         {
367             gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks;
368             gpRedCoreVol->ulAlmostFreeBlocks = 0U;
369 
370             ret = RedBufferFlush( 0U, gpRedVolume->ulBlockCount );
371 
372             if( ret == 0 )
373             {
374                 gpRedMR->hdr.ulSignature = META_SIG_METAROOT;
375                 gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence;
376 
377                 ret = RedVolSeqNumIncrement();
378             }
379 
380             if( ret == 0 )
381             {
382                 const uint8_t * pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR( gpRedMR );
383                 uint32_t ulSectorCRC;
384 
385                 #ifdef REDCONF_ENDIAN_SWAP
386                     MetaRootEndianSwap( gpRedMR );
387                 #endif
388 
389                 gpRedMR->ulSectorCRC = 0U;
390 
391                 ulSectorCRC = RedCrc32Update( 0U, &pbMR[ 8U ], gpRedVolConf->ulSectorSize - 8U );
392 
393                 if( gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE )
394                 {
395                     gpRedMR->hdr.ulCRC = RedCrc32Update( ulSectorCRC, &pbMR[ gpRedVolConf->ulSectorSize ], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize );
396                 }
397                 else
398                 {
399                     gpRedMR->hdr.ulCRC = ulSectorCRC;
400                 }
401 
402                 gpRedMR->ulSectorCRC = ulSectorCRC;
403 
404                 #ifdef REDCONF_ENDIAN_SWAP
405                     gpRedMR->hdr.ulCRC = RedRev32( gpRedMR->hdr.ulCRC );
406                     gpRedMR->ulSectorCRC = RedRev32( gpRedMR->ulSectorCRC );
407                 #endif
408 
409                 /*  Flush the block device before writing the metaroot, so that all
410                  *  previously written blocks are guaranteed to be on the media before
411                  *  the metaroot is written.  Otherwise, if the block device reorders
412                  *  the writes, the metaroot could reach the media before metadata it
413                  *  points at, creating a window for disk corruption if power is lost.
414                  */
415                 ret = RedIoFlush( gbRedVolNum );
416             }
417 
418             if( ret == 0 )
419             {
420                 ret = RedIoWrite( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR );
421 
422                 #ifdef REDCONF_ENDIAN_SWAP
423                     MetaRootEndianSwap( gpRedMR );
424                 #endif
425             }
426 
427             /*  Flush the block device to force the metaroot write to the media.  This
428              *  guarantees the transaction point is really complete before we return.
429              */
430             if( ret == 0 )
431             {
432                 ret = RedIoFlush( gbRedVolNum );
433             }
434 
435             /*  Toggle to the other metaroot buffer.  The working state and committed
436              *  state metaroot buffers exchange places.
437              */
438             if( ret == 0 )
439             {
440                 uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR;
441 
442                 gpRedCoreVol->aMR[ bNextMR ] = *gpRedMR;
443                 gpRedCoreVol->bCurMR = bNextMR;
444 
445                 gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
446 
447                 gpRedCoreVol->fBranched = false;
448             }
449 
450             CRITICAL_ASSERT( ret == 0 );
451         }
452 
453         return ret;
454     }
455 #endif /* if REDCONF_READ_ONLY == 0 */
456 
457 
458 #ifdef REDCONF_ENDIAN_SWAP
MetaRootEndianSwap(METAROOT * pMetaRoot)459     static void MetaRootEndianSwap( METAROOT * pMetaRoot )
460     {
461         if( pMetaRoot == NULL )
462         {
463             REDERROR();
464         }
465         else
466         {
467             pMetaRoot->ulSectorCRC = RedRev32( pMetaRoot->ulSectorCRC );
468             pMetaRoot->ulFreeBlocks = RedRev32( pMetaRoot->ulFreeBlocks );
469             #if REDCONF_API_POSIX == 1
470                 pMetaRoot->ulFreeInodes = RedRev32( pMetaRoot->ulFreeInodes );
471             #endif
472             pMetaRoot->ulAllocNextBlock = RedRev32( pMetaRoot->ulAllocNextBlock );
473         }
474     }
475 #endif /* ifdef REDCONF_ENDIAN_SWAP */
476 
477 
478 /** @brief Process a critical file system error.
479  *
480  *  @param pszFileName  The file in which the error occurred.
481  *  @param ulLineNum    The line number at which the error occurred.
482  */
RedVolCriticalError(const char * pszFileName,uint32_t ulLineNum)483 void RedVolCriticalError( const char * pszFileName,
484                           uint32_t ulLineNum )
485 {
486     #if REDCONF_OUTPUT == 1
487         #if REDCONF_READ_ONLY == 0
488             if( !gpRedVolume->fReadOnly )
489             {
490                 RedOsOutputString( "Critical file system error in Reliance Edge, setting volume to READONLY\n" );
491             }
492             else
493         #endif
494         {
495             RedOsOutputString( "Critical file system error in Reliance Edge (volume already READONLY)\n" );
496         }
497     #endif /* if REDCONF_OUTPUT == 1 */
498 
499     #if REDCONF_READ_ONLY == 0
500         gpRedVolume->fReadOnly = true;
501     #endif
502 
503     #if REDCONF_ASSERTS == 1
504         RedOsAssertFail( pszFileName, ulLineNum );
505     #else
506         ( void ) pszFileName;
507         ( void ) ulLineNum;
508     #endif
509 }
510 
511 
512 /** @brief Increment the sequence number.
513  *
514  *  @return A negated ::REDSTATUS code indicating the operation result.
515  *
516  *  @retval 0           Operation was successful.
517  *  @retval -RED_EINVAL Cannot increment sequence number: maximum value reached.
518  *                      This should not ever happen.
519  */
RedVolSeqNumIncrement(void)520 REDSTATUS RedVolSeqNumIncrement( void )
521 {
522     REDSTATUS ret;
523 
524     if( gpRedVolume->ullSequence == UINT64_MAX )
525     {
526         /*  In practice this should never, ever happen; to get here, there would
527          *  need to be UINT64_MAX disk writes, which would take eons: longer
528          *  than the lifetime of any product or storage media.  If this assert
529          *  fires and the current year is still written with four digits,
530          *  suspect memory corruption.
531          */
532         CRITICAL_ERROR();
533         ret = -RED_EFUBAR;
534     }
535     else
536     {
537         gpRedVolume->ullSequence++;
538         ret = 0;
539     }
540 
541     return ret;
542 }
543