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