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 inode I/O functions.
29 */
30 #include <redfs.h>
31 #include <redcore.h>
32
33
34 /* This value is used to initialize the uIndirEntry and uDindirEntry members of
35 * the CINODE structure. After seeking, a value of COORD_ENTRY_INVALID in
36 * uIndirEntry indicates that there is no indirect node in the path through the
37 * file metadata structure, and a value of COORD_ENTRY_INVALID in uDindirEntry
38 * indicates that there is no double indirect node.
39 */
40 #define COORD_ENTRY_INVALID ( UINT16_MAX )
41
42 /* This enumeration is used by the BranchBlock() and BranchBlockCost()
43 * functions to determine which blocks of the file metadata structure need to
44 * be branched, and which to ignore. DINDIR requires requires branching the
45 * double indirect only, INDIR requires branching the double indirect
46 * (if present) and the indirect, and FILE_DATA requires branching the indirect
47 * and double indirect (if present) and the file data block.
48 */
49 typedef enum
50 {
51 BRANCHDEPTH_DINDIR = 0U,
52 BRANCHDEPTH_INDIR = 1U,
53 BRANCHDEPTH_FILE_DATA = 2U,
54 BRANCHDEPTH_MAX = BRANCHDEPTH_FILE_DATA
55 } BRANCHDEPTH;
56
57
58 #if REDCONF_READ_ONLY == 0
59 #if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
60 static REDSTATUS Shrink( CINODE * pInode,
61 uint64_t ullSize );
62 #if DINDIR_POINTERS > 0U
63 static REDSTATUS TruncDindir( CINODE * pInode,
64 bool * pfFreed );
65 #endif
66 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
67 static REDSTATUS TruncIndir( CINODE * pInode,
68 bool * pfFreed );
69 #endif
70 static REDSTATUS TruncDataBlock( const CINODE * pInode,
71 uint32_t * pulBlock,
72 bool fPropagate );
73 #endif /* if DELETE_SUPPORTED || TRUNCATE_SUPPORTED */
74 static REDSTATUS ExpandPrepare( CINODE * pInode );
75 #endif /* if REDCONF_READ_ONLY == 0 */
76 static void SeekCoord( CINODE * pInode,
77 uint32_t ulBlock );
78 static REDSTATUS ReadUnaligned( CINODE * pInode,
79 uint64_t ullStart,
80 uint32_t ulLen,
81 uint8_t * pbBuffer );
82 static REDSTATUS ReadAligned( CINODE * pInode,
83 uint32_t ulBlockStart,
84 uint32_t ulBlockCount,
85 uint8_t * pbBuffer );
86 #if REDCONF_READ_ONLY == 0
87 static REDSTATUS WriteUnaligned( CINODE * pInode,
88 uint64_t ullStart,
89 uint32_t ulLen,
90 const uint8_t * pbBuffer );
91 static REDSTATUS WriteAligned( CINODE * pInode,
92 uint32_t ulBlockStart,
93 uint32_t * pulBlockCount,
94 const uint8_t * pbBuffer );
95 #endif
96 static REDSTATUS GetExtent( CINODE * pInode,
97 uint32_t ulBlockStart,
98 uint32_t * pulExtentStart,
99 uint32_t * pulExtentLen );
100 #if REDCONF_READ_ONLY == 0
101 static REDSTATUS BranchBlock( CINODE * pInode,
102 BRANCHDEPTH depth,
103 bool fBuffer );
104 static REDSTATUS BranchOneBlock( uint32_t * pulBlock,
105 void ** ppBuffer,
106 uint16_t uBFlag );
107 static REDSTATUS BranchBlockCost( const CINODE * pInode,
108 BRANCHDEPTH depth,
109 uint32_t * pulCost );
110 static uint32_t FreeBlockCount( void );
111 #endif /* if REDCONF_READ_ONLY == 0 */
112
113
114 /** @brief Read data from an inode.
115 *
116 * @param pInode A pointer to the cached inode structure of the inode from
117 * which to read.
118 * @param ullStart The file offset at which to read.
119 * @param pulLen On input, the number of bytes to attempt to read. On
120 * successful return, populated with the number of bytes
121 * actually read.
122 * @param pBuffer The buffer to read into.
123 *
124 * @return A negated ::REDSTATUS code indicating the operation result.
125 *
126 * @retval 0 Operation was successful.
127 * @retval -RED_EIO A disk I/O error occurred.
128 * @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer; or
129 * @p pulLen is `NULL`; or @p pBuffer is `NULL`.
130 */
RedInodeDataRead(CINODE * pInode,uint64_t ullStart,uint32_t * pulLen,void * pBuffer)131 REDSTATUS RedInodeDataRead( CINODE * pInode,
132 uint64_t ullStart,
133 uint32_t * pulLen,
134 void * pBuffer )
135 {
136 REDSTATUS ret = 0;
137
138 if( !CINODE_IS_MOUNTED( pInode ) || ( pulLen == NULL ) || ( pBuffer == NULL ) )
139 {
140 ret = -RED_EINVAL;
141 }
142 else if( ullStart >= pInode->pInodeBuf->ullSize )
143 {
144 *pulLen = 0U;
145 }
146 else if( *pulLen == 0U )
147 {
148 /* Do nothing, just return success.
149 */
150 }
151 else
152 {
153 uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
154 uint32_t ulReadIndex = 0U;
155 uint32_t ulLen = *pulLen;
156 uint32_t ulRemaining;
157
158 /* Reading beyond the end of the file is not allowed. If the requested
159 * read extends beyond the end of the file, truncate the read length so
160 * that the read stops at the end of the file.
161 */
162 if( ( pInode->pInodeBuf->ullSize - ullStart ) < ulLen )
163 {
164 ulLen = ( uint32_t ) ( pInode->pInodeBuf->ullSize - ullStart );
165 }
166
167 ulRemaining = ulLen;
168
169 /* Unaligned partial block at start.
170 */
171 if( ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ) != 0U )
172 {
173 uint32_t ulBytesInFirstBlock = REDCONF_BLOCK_SIZE - ( uint32_t ) ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) );
174 uint32_t ulThisRead = REDMIN( ulRemaining, ulBytesInFirstBlock );
175
176 ret = ReadUnaligned( pInode, ullStart, ulThisRead, pbBuffer );
177
178 if( ret == 0 )
179 {
180 ulReadIndex += ulThisRead;
181 ulRemaining -= ulThisRead;
182 }
183 }
184
185 /* Whole blocks.
186 */
187 if( ( ret == 0 ) && ( ulRemaining >= REDCONF_BLOCK_SIZE ) )
188 {
189 uint32_t ulBlockOffset = ( uint32_t ) ( ( ullStart + ulReadIndex ) >> BLOCK_SIZE_P2 );
190 uint32_t ulBlockCount = ulRemaining >> BLOCK_SIZE_P2;
191
192 REDASSERT( ( ( ullStart + ulReadIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
193
194 ret = ReadAligned( pInode, ulBlockOffset, ulBlockCount, &pbBuffer[ ulReadIndex ] );
195
196 if( ret == 0 )
197 {
198 ulReadIndex += ulBlockCount << BLOCK_SIZE_P2;
199 ulRemaining -= ulBlockCount << BLOCK_SIZE_P2;
200 }
201 }
202
203 /* Aligned partial block at end.
204 */
205 if( ( ret == 0 ) && ( ulRemaining > 0U ) )
206 {
207 REDASSERT( ulRemaining < REDCONF_BLOCK_SIZE );
208 REDASSERT( ( ( ullStart + ulReadIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
209
210 ret = ReadUnaligned( pInode, ullStart + ulReadIndex, ulRemaining, &pbBuffer[ ulReadIndex ] );
211 }
212
213 if( ret == 0 )
214 {
215 *pulLen = ulLen;
216 }
217 }
218
219 return ret;
220 }
221
222
223 #if REDCONF_READ_ONLY == 0
224
225 /** @brief Write to an inode.
226 *
227 * @param pInode A pointer to the cached inode structure of the inode into
228 * which to write.
229 * @param ullStart The file offset at which to write.
230 * @param pulLen On input, the number of bytes to attempt to write. On
231 * successful return, populated with the number of bytes
232 * actually written.
233 * @param pBuffer The buffer to write from.
234 *
235 * @return A negated ::REDSTATUS code indicating the operation result.
236 *
237 * @retval 0 Operation was successful.
238 * @retval -RED_EFBIG @p ullStart is greater than the maximum file size; or
239 * @p ullStart is equal to the maximum file size and the
240 * write length is non-zero.
241 * @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer; or
242 * @p pulLen is `NULL`; or @p pBuffer is `NULL`.
243 * @retval -RED_EIO A disk I/O error occurred.
244 * @retval -RED_ENOSPC No data can be written because there is insufficient
245 * free space.
246 */
RedInodeDataWrite(CINODE * pInode,uint64_t ullStart,uint32_t * pulLen,const void * pBuffer)247 REDSTATUS RedInodeDataWrite( CINODE * pInode,
248 uint64_t ullStart,
249 uint32_t * pulLen,
250 const void * pBuffer )
251 {
252 REDSTATUS ret = 0;
253
254 if( !CINODE_IS_DIRTY( pInode ) || ( pulLen == NULL ) || ( pBuffer == NULL ) )
255 {
256 ret = -RED_EINVAL;
257 }
258 else if( ( ullStart > INODE_SIZE_MAX ) || ( ( ullStart == INODE_SIZE_MAX ) && ( *pulLen > 0U ) ) )
259 {
260 ret = -RED_EFBIG;
261 }
262 else if( *pulLen == 0U )
263 {
264 /* Do nothing, just return success.
265 */
266 }
267 else
268 {
269 const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
270 uint32_t ulWriteIndex = 0U;
271 uint32_t ulLen = *pulLen;
272 uint32_t ulRemaining;
273
274 if( ( INODE_SIZE_MAX - ullStart ) < ulLen )
275 {
276 ulLen = ( uint32_t ) ( INODE_SIZE_MAX - ullStart );
277 }
278
279 ulRemaining = ulLen;
280
281 /* If the write is beyond the current end of the file, and the current
282 * end of the file is not block-aligned, then there may be some data
283 * that needs to be zeroed in the last block.
284 */
285 if( ullStart > pInode->pInodeBuf->ullSize )
286 {
287 ret = ExpandPrepare( pInode );
288 }
289
290 /* Partial block at start.
291 */
292 if( ( ret == 0 ) && ( ( ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ) != 0U ) || ( ulRemaining < REDCONF_BLOCK_SIZE ) ) )
293 {
294 uint32_t ulBytesInFirstBlock = REDCONF_BLOCK_SIZE - ( uint32_t ) ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) );
295 uint32_t ulThisWrite = REDMIN( ulRemaining, ulBytesInFirstBlock );
296
297 ret = WriteUnaligned( pInode, ullStart, ulThisWrite, pbBuffer );
298
299 if( ret == 0 )
300 {
301 ulWriteIndex += ulThisWrite;
302 ulRemaining -= ulThisWrite;
303 }
304 }
305
306 /* Whole blocks.
307 */
308 if( ( ret == 0 ) && ( ulRemaining >= REDCONF_BLOCK_SIZE ) )
309 {
310 uint32_t ulBlockOffset = ( uint32_t ) ( ( ullStart + ulWriteIndex ) >> BLOCK_SIZE_P2 );
311 uint32_t ulBlockCount = ulRemaining >> BLOCK_SIZE_P2;
312 uint32_t ulBlocksWritten = ulBlockCount;
313
314 REDASSERT( ( ( ullStart + ulWriteIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
315
316 ret = WriteAligned( pInode, ulBlockOffset, &ulBlocksWritten, &pbBuffer[ ulWriteIndex ] );
317
318 if( ( ret == -RED_ENOSPC ) && ( ulWriteIndex > 0U ) )
319 {
320 ulBlocksWritten = 0U;
321 ret = 0;
322 }
323
324 if( ret == 0 )
325 {
326 ulWriteIndex += ulBlocksWritten << BLOCK_SIZE_P2;
327 ulRemaining -= ulBlocksWritten << BLOCK_SIZE_P2;
328
329 if( ulBlocksWritten < ulBlockCount )
330 {
331 ulRemaining = 0U;
332 }
333 }
334 }
335
336 /* Partial block at end.
337 */
338 if( ( ret == 0 ) && ( ulRemaining > 0U ) )
339 {
340 REDASSERT( ulRemaining < REDCONF_BLOCK_SIZE );
341 REDASSERT( ( ( ullStart + ulWriteIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
342 REDASSERT( ulWriteIndex > 0U );
343
344 ret = WriteUnaligned( pInode, ullStart + ulWriteIndex, ulRemaining, &pbBuffer[ ulWriteIndex ] );
345
346 if( ret == -RED_ENOSPC )
347 {
348 ret = 0;
349 }
350 else if( ret == 0 )
351 {
352 ulWriteIndex += ulRemaining;
353
354 REDASSERT( ulWriteIndex == ulLen );
355 }
356 else
357 {
358 /* Unexpected error, return it.
359 */
360 }
361 }
362
363 if( ret == 0 )
364 {
365 *pulLen = ulWriteIndex;
366
367 if( ( ullStart + ulWriteIndex ) > pInode->pInodeBuf->ullSize )
368 {
369 pInode->pInodeBuf->ullSize = ullStart + ulWriteIndex;
370 }
371 }
372 }
373
374 return ret;
375 }
376
377
378 #if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
379
380 /** @brief Change the size of an inode.
381 *
382 * @param pInode A pointer to the cached inode structure.
383 * @param ullSize The new file size for the inode.
384 *
385 * @return A negated ::REDSTATUS code indicating the operation result.
386 *
387 * @retval 0 Operation was successful.
388 * @retval -RED_EFBIG @p ullSize is greater than the maximum file size.
389 * @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer.
390 * @retval -RED_EIO A disk I/O error occurred.
391 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
392 */
RedInodeDataTruncate(CINODE * pInode,uint64_t ullSize)393 REDSTATUS RedInodeDataTruncate( CINODE * pInode,
394 uint64_t ullSize )
395 {
396 REDSTATUS ret = 0;
397
398 /* The inode does not need to be dirtied when it is being deleted, because
399 * the inode buffer will be discarded without ever being written to disk.
400 * Thus, we only check to see if it's mounted here.
401 */
402 if( !CINODE_IS_MOUNTED( pInode ) )
403 {
404 ret = -RED_EINVAL;
405 }
406 else if( ullSize > INODE_SIZE_MAX )
407 {
408 ret = -RED_EFBIG;
409 }
410 else
411 {
412 if( ullSize > pInode->pInodeBuf->ullSize )
413 {
414 ret = ExpandPrepare( pInode );
415 }
416 else if( ullSize < pInode->pInodeBuf->ullSize )
417 {
418 ret = Shrink( pInode, ullSize );
419 }
420 else
421 {
422 /* Size is staying the same, nothing to do.
423 */
424 }
425
426 if( ret == 0 )
427 {
428 pInode->pInodeBuf->ullSize = ullSize;
429 }
430 }
431
432 return ret;
433 }
434
435
436 /** @brief Free all file data beyond a specified point.
437 *
438 * @param pInode A pointer to the cached inode structure.
439 * @param ullSize The point beyond which to free all file data.
440 *
441 * @return A negated ::REDSTATUS code indicating the operation result.
442 *
443 * @retval 0 Operation was successful.
444 * @retval -RED_EIO A disk I/O error occurred.
445 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
446 * @retval -RED_EINVAL Invalid parameters.
447 */
Shrink(CINODE * pInode,uint64_t ullSize)448 static REDSTATUS Shrink( CINODE * pInode,
449 uint64_t ullSize )
450 {
451 REDSTATUS ret = 0;
452
453 /* pInode->fDirty is checked explicitly here, instead of using the
454 * CINODE_IS_DIRTY() macro, to avoid a duplicate mount check.
455 */
456 if( !CINODE_IS_MOUNTED( pInode ) || ( ( ullSize > 0U ) && !pInode->fDirty ) )
457 {
458 REDERROR();
459 ret = -RED_EINVAL;
460 }
461 else
462 {
463 uint32_t ulTruncBlock = ( uint32_t ) ( ( ullSize + REDCONF_BLOCK_SIZE - 1U ) >> BLOCK_SIZE_P2 );
464
465 RedInodePutData( pInode );
466
467 #if REDCONF_DIRECT_POINTERS > 0U
468 while( ulTruncBlock < REDCONF_DIRECT_POINTERS )
469 {
470 ret = TruncDataBlock( pInode, &pInode->pInodeBuf->aulEntries[ ulTruncBlock ], true );
471
472 if( ret != 0 )
473 {
474 break;
475 }
476
477 ulTruncBlock++;
478 }
479 #endif /* if REDCONF_DIRECT_POINTERS > 0U */
480
481 #if REDCONF_INDIRECT_POINTERS > 0U
482 while( ( ret == 0 ) && ( ulTruncBlock < ( REDCONF_DIRECT_POINTERS + INODE_INDIR_BLOCKS ) ) )
483 {
484 ret = RedInodeDataSeek( pInode, ulTruncBlock );
485
486 if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
487 {
488 bool fFreed;
489
490 ret = TruncIndir( pInode, &fFreed );
491
492 if( ret == 0 )
493 {
494 if( fFreed )
495 {
496 pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = BLOCK_SPARSE;
497 }
498
499 /* The next seek will go to the beginning of the next
500 * indirect.
501 */
502 ulTruncBlock += ( INDIR_ENTRIES - pInode->uIndirEntry );
503 }
504 }
505 }
506 #endif /* if REDCONF_INDIRECT_POINTERS > 0U */
507
508 #if DINDIR_POINTERS > 0U
509 while( ( ret == 0 ) && ( ulTruncBlock < INODE_DATA_BLOCKS ) )
510 {
511 ret = RedInodeDataSeek( pInode, ulTruncBlock );
512
513 if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
514 {
515 bool fFreed;
516
517 /* TruncDindir() invokes seek as it goes along, which will
518 * update the entry values (possibly all three of these);
519 * make a copy so we can compute things correctly after.
520 */
521 uint16_t uOrigInodeEntry = pInode->uInodeEntry;
522 uint16_t uOrigDindirEntry = pInode->uDindirEntry;
523 uint16_t uOrigIndirEntry = pInode->uIndirEntry;
524
525 ret = TruncDindir( pInode, &fFreed );
526
527 if( ret == 0 )
528 {
529 if( fFreed )
530 {
531 pInode->pInodeBuf->aulEntries[ uOrigInodeEntry ] = BLOCK_SPARSE;
532 }
533
534 /* The next seek will go to the beginning of the next
535 * double indirect.
536 */
537 ulTruncBlock += ( DINDIR_DATA_BLOCKS - ( uOrigDindirEntry * INDIR_ENTRIES ) ) - uOrigIndirEntry;
538 }
539 }
540 }
541 #endif /* if DINDIR_POINTERS > 0U */
542 }
543
544 return ret;
545 }
546
547
548 #if DINDIR_POINTERS > 0U
549
550 /** @brief Truncate a double indirect.
551 *
552 * @param pInode A pointer to the cached inode, whose coordinates indicate
553 * the truncation boundary.
554 * @param pfFreed On successful return, populated with whether the double
555 * indirect node was freed.
556 *
557 * @return A negated ::REDSTATUS code indicating the operation result.
558 *
559 * @retval 0 Operation was successful.
560 * @retval -RED_EIO A disk I/O error occurred.
561 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
562 * @retval -RED_EINVAL Invalid parameters.
563 */
TruncDindir(CINODE * pInode,bool * pfFreed)564 static REDSTATUS TruncDindir( CINODE * pInode,
565 bool * pfFreed )
566 {
567 REDSTATUS ret = 0;
568
569 if( !CINODE_IS_MOUNTED( pInode ) || ( pfFreed == NULL ) )
570 {
571 REDERROR();
572 ret = -RED_EINVAL;
573 }
574 else if( pInode->pDindir == NULL )
575 {
576 *pfFreed = false;
577 }
578 else
579 {
580 bool fBranch = false;
581 uint16_t uEntry;
582
583 /* The double indirect is definitely going to be branched (instead of
584 * deleted) if any of its indirect pointers which are entirely prior to
585 * the truncation boundary are non-sparse.
586 */
587 for( uEntry = 0U; !fBranch && ( uEntry < pInode->uDindirEntry ); uEntry++ )
588 {
589 fBranch = pInode->pDindir->aulEntries[ uEntry ] != BLOCK_SPARSE;
590 }
591
592 /* Unless we already know for a fact that the double indirect is going
593 * to be branched, examine the contents of the indirect pointer which
594 * straddles the truncation boundary. If the indirect is going to be
595 * deleted, we know this indirect pointer is going away, and that might
596 * mean the double indirect is going to be deleted also.
597 */
598 if( !fBranch && ( pInode->pDindir->aulEntries[ pInode->uDindirEntry ] != BLOCK_SPARSE ) )
599 {
600 for( uEntry = 0U; !fBranch && ( uEntry < pInode->uIndirEntry ); uEntry++ )
601 {
602 fBranch = pInode->pIndir->aulEntries[ uEntry ] != BLOCK_SPARSE;
603 }
604 }
605
606 if( fBranch )
607 {
608 ret = BranchBlock( pInode, BRANCHDEPTH_DINDIR, false );
609 }
610
611 if( ret == 0 )
612 {
613 uint32_t ulBlock = pInode->ulLogicalBlock;
614 uint16_t uStart = pInode->uDindirEntry; /* pInode->uDindirEntry will change. */
615
616 for( uEntry = uStart; uEntry < INDIR_ENTRIES; uEntry++ )
617 {
618 /* Seek so that TruncIndir() has the correct indirect
619 * buffer and indirect entry.
620 */
621 ret = RedInodeDataSeek( pInode, ulBlock );
622
623 if( ret == -RED_ENODATA )
624 {
625 ret = 0;
626 }
627
628 if( ( ret == 0 ) && ( pInode->ulIndirBlock != BLOCK_SPARSE ) )
629 {
630 bool fIndirFreed;
631
632 ret = TruncIndir( pInode, &fIndirFreed );
633
634 if( ret == 0 )
635 {
636 /* All of the indirects after the one which straddles
637 * the truncation boundary should definitely end up
638 * deleted.
639 */
640 REDASSERT( ( uEntry == uStart ) || fIndirFreed );
641
642 /* If the double indirect is being freed, all of the
643 * indirects should be freed too.
644 */
645 REDASSERT( fIndirFreed || fBranch );
646
647 if( fBranch && fIndirFreed )
648 {
649 pInode->pDindir->aulEntries[ uEntry ] = BLOCK_SPARSE;
650 }
651 }
652 }
653
654 if( ret != 0 )
655 {
656 break;
657 }
658
659 ulBlock += ( INDIR_ENTRIES - pInode->uIndirEntry );
660 }
661
662 if( ret == 0 )
663 {
664 *pfFreed = !fBranch;
665
666 if( !fBranch )
667 {
668 RedInodePutDindir( pInode );
669
670 ret = RedImapBlockSet( pInode->ulDindirBlock, false );
671 }
672 }
673 }
674 }
675
676 return ret;
677 }
678 #endif /* DINDIR_POINTERS > 0U */
679
680
681 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
682
683 /** @brief Truncate a indirect.
684 *
685 * @param pInode A pointer to the cached inode, whose coordinates indicate
686 * the truncation boundary.
687 * @param pfFreed On successful return, populated with whether the indirect
688 * node was freed.
689 *
690 * @return A negated ::REDSTATUS code indicating the operation result.
691 *
692 * @retval 0 Operation was successful.
693 * @retval -RED_EIO A disk I/O error occurred.
694 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
695 * @retval -RED_EINVAL Invalid parameters.
696 */
TruncIndir(CINODE * pInode,bool * pfFreed)697 static REDSTATUS TruncIndir( CINODE * pInode,
698 bool * pfFreed )
699 {
700 REDSTATUS ret = 0;
701
702 if( !CINODE_IS_MOUNTED( pInode ) || ( pfFreed == NULL ) )
703 {
704 REDERROR();
705 ret = -RED_EINVAL;
706 }
707 else if( pInode->pIndir == NULL )
708 {
709 *pfFreed = false;
710 }
711 else
712 {
713 bool fBranch = false;
714 uint16_t uEntry;
715
716 /* Scan the range of entries which are not being truncated. If there
717 * is anything there, then the indirect will not be empty after the
718 * truncate, so it is branched and modified instead of deleted.
719 */
720 for( uEntry = 0U; !fBranch && ( uEntry < pInode->uIndirEntry ); uEntry++ )
721 {
722 fBranch = pInode->pIndir->aulEntries[ uEntry ] != BLOCK_SPARSE;
723 }
724
725 if( fBranch )
726 {
727 ret = BranchBlock( pInode, BRANCHDEPTH_INDIR, false );
728 }
729
730 if( ret == 0 )
731 {
732 for( uEntry = pInode->uIndirEntry; uEntry < INDIR_ENTRIES; uEntry++ )
733 {
734 ret = TruncDataBlock( pInode, &pInode->pIndir->aulEntries[ uEntry ], fBranch );
735
736 if( ret != 0 )
737 {
738 break;
739 }
740 }
741
742 if( ret == 0 )
743 {
744 *pfFreed = !fBranch;
745
746 if( !fBranch )
747 {
748 RedInodePutIndir( pInode );
749
750 ret = RedImapBlockSet( pInode->ulIndirBlock, false );
751 }
752 }
753 }
754 }
755
756 return ret;
757 }
758 #endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
759
760
761 /** @brief Truncate a file data block.
762 *
763 * @param pInode A pointer to the cached inode structure.
764 * @param pulBlock On entry, contains the block to be truncated. On
765 * successful return, if @p fPropagate is true, populated
766 * with BLOCK_SPARSE, otherwise unmodified.
767 * @param fPropagate Whether the parent node is being branched.
768 *
769 * @return A negated ::REDSTATUS code indicating the operation result.
770 *
771 * @retval 0 Operation was successful.
772 * @retval -RED_EIO A disk I/O error occurred.
773 * @retval -RED_EINVAL Invalid parameters.
774 */
TruncDataBlock(const CINODE * pInode,uint32_t * pulBlock,bool fPropagate)775 static REDSTATUS TruncDataBlock( const CINODE * pInode,
776 uint32_t * pulBlock,
777 bool fPropagate )
778 {
779 REDSTATUS ret = 0;
780
781 if( !CINODE_IS_MOUNTED( pInode ) || ( pulBlock == NULL ) )
782 {
783 REDERROR();
784 ret = -RED_EINVAL;
785 }
786 else if( *pulBlock != BLOCK_SPARSE )
787 {
788 ret = RedImapBlockSet( *pulBlock, false );
789
790 #if REDCONF_INODE_BLOCKS == 1
791 if( ret == 0 )
792 {
793 if( pInode->pInodeBuf->ulBlocks == 0U )
794 {
795 CRITICAL_ERROR();
796 ret = -RED_EFUBAR;
797 }
798 else
799 {
800 pInode->pInodeBuf->ulBlocks--;
801 }
802 }
803 #endif /* if REDCONF_INODE_BLOCKS == 1 */
804
805 if( ( ret == 0 ) && fPropagate )
806 {
807 *pulBlock = BLOCK_SPARSE;
808 }
809 }
810 else
811 {
812 /* Data block is sparse, nothing to truncate.
813 */
814 }
815
816 return ret;
817 }
818 #endif /* DELETE_SUPPORTED || TRUNCATE_SUPPORTED */
819
820
821 /** @brief Prepare to increase the file size.
822 *
823 * When the inode size is increased, a sparse region is created. It is
824 * possible that a prior shrink operation to an unaligned size left stale data
825 * beyond the end of the file in the last data block. That data is not zeroed
826 * while shrinking the inode in order to transfer the disk full burden from the
827 * shrink operation to the expand operation.
828 *
829 * @param pInode A pointer to the cached inode structure.
830 *
831 * @return A negated ::REDSTATUS code indicating the operation result.
832 *
833 * @retval 0 Operation was successful.
834 * @retval -RED_EIO A disk I/O error occurred.
835 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
836 * @retval -RED_EINVAL Invalid parameters.
837 */
ExpandPrepare(CINODE * pInode)838 static REDSTATUS ExpandPrepare( CINODE * pInode )
839 {
840 REDSTATUS ret = 0;
841
842 if( !CINODE_IS_DIRTY( pInode ) )
843 {
844 REDERROR();
845 ret = -RED_EINVAL;
846 }
847 else
848 {
849 uint32_t ulOldSizeByteInBlock = ( uint32_t ) ( pInode->pInodeBuf->ullSize & ( REDCONF_BLOCK_SIZE - 1U ) );
850
851 if( ulOldSizeByteInBlock != 0U )
852 {
853 ret = RedInodeDataSeek( pInode, ( uint32_t ) ( pInode->pInodeBuf->ullSize >> BLOCK_SIZE_P2 ) );
854
855 if( ret == -RED_ENODATA )
856 {
857 ret = 0;
858 }
859 else if( ret == 0 )
860 {
861 ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, true );
862
863 if( ret == 0 )
864 {
865 RedMemSet( &pInode->pbData[ ulOldSizeByteInBlock ], 0U, REDCONF_BLOCK_SIZE - ulOldSizeByteInBlock );
866 }
867 }
868 else
869 {
870 REDERROR();
871 }
872 }
873 }
874
875 return ret;
876 }
877 #endif /* REDCONF_READ_ONLY == 0 */
878
879
880 /** @brief Seek to a given position within an inode, then buffer the data block.
881 *
882 * On successful return, pInode->pbData will be populated with a buffer
883 * corresponding to the @p ulBlock block offset.
884 *
885 * @param pInode A pointer to the cached inode structure.
886 * @param ulBlock The block offset to seek to and buffer.
887 *
888 * @return A negated ::REDSTATUS code indicating the operation result.
889 *
890 * @retval 0 Operation was successful.
891 * @retval -RED_ENODATA The block offset is sparse.
892 * @retval -RED_EINVAL @p ulBlock is too large.
893 * @retval -RED_EIO A disk I/O error occurred.
894 */
RedInodeDataSeekAndRead(CINODE * pInode,uint32_t ulBlock)895 REDSTATUS RedInodeDataSeekAndRead( CINODE * pInode,
896 uint32_t ulBlock )
897 {
898 REDSTATUS ret;
899
900 ret = RedInodeDataSeek( pInode, ulBlock );
901
902 if( ( ret == 0 ) && ( pInode->pbData == NULL ) )
903 {
904 REDASSERT( pInode->ulDataBlock != BLOCK_SPARSE );
905
906 ret = RedBufferGet( pInode->ulDataBlock, 0U, CAST_VOID_PTR_PTR( &pInode->pbData ) );
907 }
908
909 return ret;
910 }
911
912
913 /** @brief Seek to a given position within an inode.
914 *
915 * On successful return, pInode->ulDataBlock will be populated with the
916 * physical block number corresponding to the @p ulBlock block offset.
917 *
918 * Note: Callers of this function depend on its parameter checking.
919 *
920 * @param pInode A pointer to the cached inode structure.
921 * @param ulBlock The block offset to seek to.
922 *
923 * @return A negated ::REDSTATUS code indicating the operation result.
924 *
925 * @retval 0 Operation was successful.
926 * @retval -RED_ENODATA The block offset is sparse.
927 * @retval -RED_EINVAL @p ulBlock is too large; or @p pInode is not a
928 * mounted cached inode pointer.
929 * @retval -RED_EIO A disk I/O error occurred.
930 */
RedInodeDataSeek(CINODE * pInode,uint32_t ulBlock)931 REDSTATUS RedInodeDataSeek( CINODE * pInode,
932 uint32_t ulBlock )
933 {
934 REDSTATUS ret = 0;
935
936 if( !CINODE_IS_MOUNTED( pInode ) || ( ulBlock >= INODE_DATA_BLOCKS ) )
937 {
938 ret = -RED_EINVAL;
939 }
940 else
941 {
942 SeekCoord( pInode, ulBlock );
943
944 #if DINDIR_POINTERS > 0U
945 if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
946 {
947 if( pInode->ulDindirBlock == BLOCK_SPARSE )
948 {
949 /* If the double indirect is unallocated, so is the indirect.
950 */
951 pInode->ulIndirBlock = BLOCK_SPARSE;
952 }
953 else
954 {
955 if( pInode->pDindir == NULL )
956 {
957 ret = RedBufferGet( pInode->ulDindirBlock, BFLAG_META_DINDIR, CAST_VOID_PTR_PTR( &pInode->pDindir ) );
958 }
959
960 if( ret == 0 )
961 {
962 pInode->ulIndirBlock = pInode->pDindir->aulEntries[ pInode->uDindirEntry ];
963 }
964 }
965 }
966 #endif /* if DINDIR_POINTERS > 0U */
967
968 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
969 if( ( ret == 0 ) && ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) )
970 {
971 if( pInode->ulIndirBlock == BLOCK_SPARSE )
972 {
973 /* If the indirect is unallocated, so is the data block.
974 */
975 pInode->ulDataBlock = BLOCK_SPARSE;
976 }
977 else
978 {
979 if( pInode->pIndir == NULL )
980 {
981 ret = RedBufferGet( pInode->ulIndirBlock, BFLAG_META_INDIR, CAST_VOID_PTR_PTR( &pInode->pIndir ) );
982 }
983
984 if( ret == 0 )
985 {
986 pInode->ulDataBlock = pInode->pIndir->aulEntries[ pInode->uIndirEntry ];
987 }
988 }
989 }
990 #endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
991
992 if( ( ret == 0 ) && ( pInode->ulDataBlock == BLOCK_SPARSE ) )
993 {
994 ret = -RED_ENODATA;
995 }
996 }
997
998 return ret;
999 }
1000
1001
1002 /** @brief Seek to the coordinates.
1003 *
1004 * Compute the new coordinates, and put any buffers which are not needed or are
1005 * no longer appropriate.
1006 *
1007 * @param pInode A pointer to the cached inode structure.
1008 * @param ulBlock The block offset to seek to.
1009 */
SeekCoord(CINODE * pInode,uint32_t ulBlock)1010 static void SeekCoord( CINODE * pInode,
1011 uint32_t ulBlock )
1012 {
1013 if( !CINODE_IS_MOUNTED( pInode ) || ( ulBlock >= INODE_DATA_BLOCKS ) )
1014 {
1015 REDERROR();
1016 }
1017 else if( ( pInode->ulLogicalBlock != ulBlock ) || !pInode->fCoordInited )
1018 {
1019 RedInodePutData( pInode );
1020 pInode->ulLogicalBlock = ulBlock;
1021
1022 #if REDCONF_DIRECT_POINTERS > 0U
1023 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1024 if( ulBlock < REDCONF_DIRECT_POINTERS )
1025 #endif
1026 {
1027 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1028 RedInodePutCoord( pInode );
1029 #endif
1030
1031 pInode->uInodeEntry = ( uint16_t ) ulBlock;
1032 pInode->ulDataBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
1033
1034 #if DINDIR_POINTERS > 0U
1035 pInode->uDindirEntry = COORD_ENTRY_INVALID;
1036 #endif
1037 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1038 pInode->uIndirEntry = COORD_ENTRY_INVALID;
1039 #endif
1040 }
1041
1042 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1043 else
1044 #endif
1045 #endif /* if REDCONF_DIRECT_POINTERS > 0U */
1046 #if REDCONF_INDIRECT_POINTERS > 0U
1047 #if REDCONF_INDIRECT_POINTERS < INODE_ENTRIES
1048 if( ulBlock < ( INODE_INDIR_BLOCKS + REDCONF_DIRECT_POINTERS ) )
1049 #endif
1050 {
1051 uint32_t ulIndirRangeOffset = ulBlock - REDCONF_DIRECT_POINTERS;
1052 uint16_t uInodeEntry = ( uint16_t ) ( ( ulIndirRangeOffset / INDIR_ENTRIES ) + REDCONF_DIRECT_POINTERS );
1053 uint16_t uIndirEntry = ( uint16_t ) ( ulIndirRangeOffset % INDIR_ENTRIES );
1054
1055 #if DINDIR_POINTERS > 0U
1056 RedInodePutDindir( pInode );
1057 #endif
1058
1059 /* If the inode entry is not changing, then the previous indirect
1060 * is still the correct one. Otherwise, the old indirect will be
1061 * released and the new one will be read later.
1062 */
1063 if( ( pInode->uInodeEntry != uInodeEntry ) || !pInode->fCoordInited )
1064 {
1065 RedInodePutIndir( pInode );
1066
1067 pInode->uInodeEntry = uInodeEntry;
1068
1069 pInode->ulIndirBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
1070 }
1071
1072 #if DINDIR_POINTERS > 0U
1073 pInode->uDindirEntry = COORD_ENTRY_INVALID;
1074 #endif
1075 pInode->uIndirEntry = uIndirEntry;
1076
1077 /* At this point, the following pInode members are needed but not
1078 * yet populated:
1079 *
1080 * - pIndir
1081 * - ulDataBlock
1082 */
1083 }
1084
1085 #if DINDIR_POINTERS > 0U
1086 else
1087 #endif
1088 #endif /* if REDCONF_INDIRECT_POINTERS > 0U */
1089 #if DINDIR_POINTERS > 0U
1090 {
1091 uint32_t ulDindirRangeOffset = ( ulBlock - REDCONF_DIRECT_POINTERS ) - INODE_INDIR_BLOCKS;
1092 uint16_t uInodeEntry = ( uint16_t ) ( ( ulDindirRangeOffset / DINDIR_DATA_BLOCKS ) + REDCONF_DIRECT_POINTERS + REDCONF_INDIRECT_POINTERS );
1093 uint32_t ulDindirNodeOffset = ulDindirRangeOffset % DINDIR_DATA_BLOCKS;
1094 uint16_t uDindirEntry = ( uint16_t ) ( ulDindirNodeOffset / INDIR_ENTRIES );
1095 uint16_t uIndirEntry = ( uint16_t ) ( ulDindirNodeOffset % INDIR_ENTRIES );
1096
1097 /* If the inode entry is not changing, then the previous double
1098 * indirect is still the correct one. Otherwise, the old double
1099 * indirect will be released and the new one will be read later.
1100 */
1101 if( ( pInode->uInodeEntry != uInodeEntry ) || !pInode->fCoordInited )
1102 {
1103 RedInodePutIndir( pInode );
1104 RedInodePutDindir( pInode );
1105
1106 pInode->uInodeEntry = uInodeEntry;
1107
1108 pInode->ulDindirBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
1109 }
1110
1111 /* If neither the inode entry nor double indirect entry are
1112 * changing, then the previous indirect is still the correct one.
1113 * Otherwise, it old indirect will be released and the new one will
1114 * be read later.
1115 */
1116 else if( pInode->uDindirEntry != uDindirEntry )
1117 {
1118 RedInodePutIndir( pInode );
1119 }
1120 else
1121 {
1122 /* Data buffer has already been put, nothing to do.
1123 */
1124 }
1125
1126 pInode->uDindirEntry = uDindirEntry;
1127 pInode->uIndirEntry = uIndirEntry;
1128
1129 /* At this point, the following pInode members are needed but not
1130 * yet populated:
1131 *
1132 * - pDindir
1133 * - pIndir
1134 * - ulIndirBlock
1135 * - ulDataBlock
1136 */
1137 }
1138 #elif ( REDCONF_DIRECT_POINTERS > 0U ) && ( REDCONF_INDIRECT_POINTERS > 0U )
1139 else
1140 {
1141 /* There are no double indirects, so the block should have been in
1142 * the direct or indirect range.
1143 */
1144 REDERROR();
1145 }
1146 #endif /* if DINDIR_POINTERS > 0U */
1147
1148 pInode->fCoordInited = true;
1149 }
1150 else
1151 {
1152 /* Seeking to the current position, nothing to do.
1153 */
1154 }
1155 }
1156
1157
1158 /** @brief Read an unaligned portion of a block.
1159 *
1160 * @param pInode A pointer to the cached inode structure.
1161 * @param ullStart The file offset at which to read.
1162 * @param ulLen The number of bytes to read.
1163 * @param pbBuffer The buffer to read into.
1164 *
1165 * @return A negated ::REDSTATUS code indicating the operation result.
1166 *
1167 * @retval 0 Operation was successful.
1168 * @retval -RED_EIO A disk I/O error occurred.
1169 * @retval -RED_EINVAL Invalid parameters.
1170 */
ReadUnaligned(CINODE * pInode,uint64_t ullStart,uint32_t ulLen,uint8_t * pbBuffer)1171 static REDSTATUS ReadUnaligned( CINODE * pInode,
1172 uint64_t ullStart,
1173 uint32_t ulLen,
1174 uint8_t * pbBuffer )
1175 {
1176 REDSTATUS ret;
1177
1178 /* This read should not cross a block boundary.
1179 */
1180 if( ( ( ullStart >> BLOCK_SIZE_P2 ) != ( ( ( ullStart + ulLen ) - 1U ) >> BLOCK_SIZE_P2 ) ) ||
1181 ( pbBuffer == NULL ) )
1182 {
1183 REDERROR();
1184 ret = -RED_EINVAL;
1185 }
1186 else
1187 {
1188 ret = RedInodeDataSeekAndRead( pInode, ( uint32_t ) ( ullStart >> BLOCK_SIZE_P2 ) );
1189
1190 if( ret == 0 )
1191 {
1192 RedMemCpy( pbBuffer, &pInode->pbData[ ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ], ulLen );
1193 }
1194 else if( ret == -RED_ENODATA )
1195 {
1196 /* Sparse block, return zeroed data.
1197 */
1198 RedMemSet( pbBuffer, 0U, ulLen );
1199 ret = 0;
1200 }
1201 else
1202 {
1203 /* No action, just return the error.
1204 */
1205 }
1206 }
1207
1208 return ret;
1209 }
1210
1211
1212 /** @brief Read one or more whole blocks.
1213 *
1214 * @param pInode A pointer to the cached inode structure.
1215 * @param ulBlockStart The file block offset at which to read.
1216 * @param ulBlockCount The number of blocks to read.
1217 * @param pbBuffer The buffer to read into.
1218 *
1219 * @return A negated ::REDSTATUS code indicating the operation result.
1220 *
1221 * @retval 0 Operation was successful.
1222 * @retval -RED_EIO A disk I/O error occurred.
1223 * @retval -RED_EINVAL Invalid parameters.
1224 */
ReadAligned(CINODE * pInode,uint32_t ulBlockStart,uint32_t ulBlockCount,uint8_t * pbBuffer)1225 static REDSTATUS ReadAligned( CINODE * pInode,
1226 uint32_t ulBlockStart,
1227 uint32_t ulBlockCount,
1228 uint8_t * pbBuffer )
1229 {
1230 REDSTATUS ret = 0;
1231
1232 if( pbBuffer == NULL )
1233 {
1234 REDERROR();
1235 ret = -RED_EINVAL;
1236 }
1237 else
1238 {
1239 uint32_t ulBlockIndex = 0U;
1240
1241 /* Read the data from disk one contiguous extent at a time.
1242 */
1243 while( ( ret == 0 ) && ( ulBlockIndex < ulBlockCount ) )
1244 {
1245 uint32_t ulExtentStart;
1246 uint32_t ulExtentLen = ulBlockCount - ulBlockIndex;
1247
1248 ret = GetExtent( pInode, ulBlockStart + ulBlockIndex, &ulExtentStart, &ulExtentLen );
1249
1250 if( ret == 0 )
1251 {
1252 #if REDCONF_READ_ONLY == 0
1253
1254 /* Before reading directly from disk, flush any dirty file data
1255 * buffers in the range to avoid reading stale data.
1256 */
1257 ret = RedBufferFlush( ulExtentStart, ulExtentLen );
1258
1259 if( ret == 0 )
1260 #endif
1261 {
1262 ret = RedIoRead( gbRedVolNum, ulExtentStart, ulExtentLen, &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ] );
1263
1264 if( ret == 0 )
1265 {
1266 ulBlockIndex += ulExtentLen;
1267 }
1268 }
1269 }
1270 else if( ret == -RED_ENODATA )
1271 {
1272 /* Sparse block, return zeroed data.
1273 */
1274 RedMemSet( &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ], 0U, REDCONF_BLOCK_SIZE );
1275 ulBlockIndex++;
1276 ret = 0;
1277 }
1278 else
1279 {
1280 /* An unexpected error occurred; the loop will terminate.
1281 */
1282 }
1283 }
1284 }
1285
1286 return ret;
1287 }
1288
1289
1290 #if REDCONF_READ_ONLY == 0
1291
1292 /** @brief Write an unaligned portion of a block.
1293 *
1294 * @param pInode A pointer to the cached inode structure.
1295 * @param ullStart The file offset at which to write.
1296 * @param ulLen The number of bytes to write.
1297 * @param pbBuffer The buffer to write from.
1298 *
1299 * @return A negated ::REDSTATUS code indicating the operation result.
1300 *
1301 * @retval 0 Operation was successful.
1302 * @retval -RED_EIO A disk I/O error occurred.
1303 * @retval -RED_ENOSPC No data can be written because there is insufficient
1304 * free space.
1305 * @retval -RED_EINVAL Invalid parameters.
1306 */
WriteUnaligned(CINODE * pInode,uint64_t ullStart,uint32_t ulLen,const uint8_t * pbBuffer)1307 static REDSTATUS WriteUnaligned( CINODE * pInode,
1308 uint64_t ullStart,
1309 uint32_t ulLen,
1310 const uint8_t * pbBuffer )
1311 {
1312 REDSTATUS ret;
1313
1314 /* This write should not cross a block boundary.
1315 */
1316 if( ( ( ullStart >> BLOCK_SIZE_P2 ) != ( ( ( ullStart + ulLen ) - 1U ) >> BLOCK_SIZE_P2 ) ) ||
1317 ( pbBuffer == NULL ) )
1318 {
1319 REDERROR();
1320 ret = -RED_EINVAL;
1321 }
1322 else
1323 {
1324 ret = RedInodeDataSeek( pInode, ( uint32_t ) ( ullStart >> BLOCK_SIZE_P2 ) );
1325
1326 if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
1327 {
1328 ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, true );
1329
1330 if( ret == 0 )
1331 {
1332 RedMemCpy( &pInode->pbData[ ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ], pbBuffer, ulLen );
1333 }
1334 }
1335 }
1336
1337 return ret;
1338 }
1339
1340
1341 /** @brief Write one or more whole blocks.
1342 *
1343 * @param pInode A pointer to the cached inode structure.
1344 * @param ulBlockStart The file block offset at which to write.
1345 * @param pulBlockCount On entry, the number of blocks to attempt to write.
1346 * On successful return, the number of blocks actually
1347 * written.
1348 * @param pbBuffer The buffer to write from.
1349 *
1350 * @return A negated ::REDSTATUS code indicating the operation result.
1351 *
1352 * @retval 0 Operation was successful.
1353 * @retval -RED_EIO A disk I/O error occurred.
1354 * @retval -RED_ENOSPC No data can be written because there is insufficient
1355 * free space.
1356 * @retval -RED_EINVAL Invalid parameters.
1357 */
WriteAligned(CINODE * pInode,uint32_t ulBlockStart,uint32_t * pulBlockCount,const uint8_t * pbBuffer)1358 static REDSTATUS WriteAligned( CINODE * pInode,
1359 uint32_t ulBlockStart,
1360 uint32_t * pulBlockCount,
1361 const uint8_t * pbBuffer )
1362 {
1363 REDSTATUS ret = 0;
1364
1365 if( ( pulBlockCount == NULL ) || ( pbBuffer == NULL ) )
1366 {
1367 REDERROR();
1368 ret = -RED_EINVAL;
1369 }
1370 else
1371 {
1372 bool fFull = false;
1373 uint32_t ulBlockCount = *pulBlockCount;
1374 uint32_t ulBlockIndex;
1375
1376 /* Branch all of the file data blocks in advance.
1377 */
1378 for( ulBlockIndex = 0U; ( ulBlockIndex < ulBlockCount ) && !fFull; ulBlockIndex++ )
1379 {
1380 ret = RedInodeDataSeek( pInode, ulBlockStart + ulBlockIndex );
1381
1382 if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
1383 {
1384 ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, false );
1385
1386 if( ret == -RED_ENOSPC )
1387 {
1388 if( ulBlockIndex > 0U )
1389 {
1390 ret = 0;
1391 }
1392
1393 fFull = true;
1394 }
1395 }
1396
1397 if( ret != 0 )
1398 {
1399 break;
1400 }
1401 }
1402
1403 ulBlockCount = ulBlockIndex;
1404 ulBlockIndex = 0U;
1405
1406 if( fFull )
1407 {
1408 ulBlockCount--;
1409 }
1410
1411 /* Write the data to disk one contiguous extent at a time.
1412 */
1413 while( ( ret == 0 ) && ( ulBlockIndex < ulBlockCount ) )
1414 {
1415 uint32_t ulExtentStart;
1416 uint32_t ulExtentLen = ulBlockCount - ulBlockIndex;
1417
1418 ret = GetExtent( pInode, ulBlockStart + ulBlockIndex, &ulExtentStart, &ulExtentLen );
1419
1420 if( ret == 0 )
1421 {
1422 ret = RedIoWrite( gbRedVolNum, ulExtentStart, ulExtentLen, &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ] );
1423
1424 if( ret == 0 )
1425 {
1426 /* If there is any buffered file data for the extent we
1427 * just wrote, those buffers are now stale.
1428 */
1429 ret = RedBufferDiscardRange( ulExtentStart, ulExtentLen );
1430 }
1431
1432 if( ret == 0 )
1433 {
1434 ulBlockIndex += ulExtentLen;
1435 }
1436 }
1437 }
1438
1439 if( ret == 0 )
1440 {
1441 *pulBlockCount = ulBlockCount;
1442 }
1443 }
1444
1445 return ret;
1446 }
1447 #endif /* REDCONF_READ_ONLY == 0 */
1448
1449
1450 /** @brief Get the physical block number and count of contiguous blocks given a
1451 * starting logical block number.
1452 *
1453 * @param pInode A pointer to the cached inode structure.
1454 * @param ulBlockStart The file block offset for the start of the extent.
1455 * @param pulExtentStart On successful return, the starting physical block
1456 * number of the contiguous extent.
1457 * @param pulExtentLen On entry, the maximum length of the extent; on
1458 * successful return, the length of the contiguous
1459 * extent.
1460 *
1461 * @return A negated ::REDSTATUS code indicating the operation result.
1462 *
1463 * @retval 0 Operation was successful.
1464 * @retval -RED_EIO A disk I/O error occurred.
1465 * @retval -RED_ENODATA The block offset is sparse.
1466 * @retval -RED_EINVAL Invalid parameters.
1467 */
GetExtent(CINODE * pInode,uint32_t ulBlockStart,uint32_t * pulExtentStart,uint32_t * pulExtentLen)1468 static REDSTATUS GetExtent( CINODE * pInode,
1469 uint32_t ulBlockStart,
1470 uint32_t * pulExtentStart,
1471 uint32_t * pulExtentLen )
1472 {
1473 REDSTATUS ret;
1474
1475 if( ( pulExtentStart == NULL ) || ( pulExtentLen == NULL ) )
1476 {
1477 REDERROR();
1478 ret = -RED_EINVAL;
1479 }
1480 else
1481 {
1482 ret = RedInodeDataSeek( pInode, ulBlockStart );
1483
1484 if( ret == 0 )
1485 {
1486 uint32_t ulExtentLen = *pulExtentLen;
1487 uint32_t ulFirstBlock = pInode->ulDataBlock;
1488 uint32_t ulRunLen = 1U;
1489
1490 while( ( ret == 0 ) && ( ulRunLen < ulExtentLen ) )
1491 {
1492 ret = RedInodeDataSeek( pInode, ulBlockStart + ulRunLen );
1493
1494 /* The extent ends when we find a sparse data block or when the
1495 * data block is not contiguous with the preceding data block.
1496 */
1497 if( ( ret == -RED_ENODATA ) || ( ( ret == 0 ) && ( pInode->ulDataBlock != ( ulFirstBlock + ulRunLen ) ) ) )
1498 {
1499 ret = 0;
1500 break;
1501 }
1502
1503 ulRunLen++;
1504 }
1505
1506 if( ret == 0 )
1507 {
1508 *pulExtentStart = ulFirstBlock;
1509 *pulExtentLen = ulRunLen;
1510 }
1511 }
1512 }
1513
1514 return ret;
1515 }
1516
1517
1518 #if REDCONF_READ_ONLY == 0
1519
1520 /** @brief Allocate or branch the file metadata path and data block if necessary.
1521 *
1522 * Optionally, can stop allocating/branching at a certain depth.
1523 *
1524 * @param pInode A pointer to the cached inode structure.
1525 * @param depth A BRANCHDEPTH_ value indicating the lowest depth to branch.
1526 * @param fBuffer Whether to buffer the data block.
1527 *
1528 * @return A negated ::REDSTATUS code indicating the operation result.
1529 *
1530 * @retval 0 Operation was successful.
1531 * @retval -RED_EIO A disk I/O error occurred.
1532 * @retval -RED_ENOSPC No data can be written because there is insufficient
1533 * free space.
1534 */
BranchBlock(CINODE * pInode,BRANCHDEPTH depth,bool fBuffer)1535 static REDSTATUS BranchBlock( CINODE * pInode,
1536 BRANCHDEPTH depth,
1537 bool fBuffer )
1538 {
1539 REDSTATUS ret;
1540 uint32_t ulCost = 0U; /* Init'd to quiet warnings. */
1541
1542 ret = BranchBlockCost( pInode, depth, &ulCost );
1543
1544 if( ( ret == 0 ) && ( ulCost > FreeBlockCount() ) )
1545 {
1546 ret = -RED_ENOSPC;
1547 }
1548
1549 if( ret == 0 )
1550 {
1551 #if DINDIR_POINTERS > 0U
1552 if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
1553 {
1554 ret = BranchOneBlock( &pInode->ulDindirBlock, CAST_VOID_PTR_PTR( &pInode->pDindir ), BFLAG_META_DINDIR );
1555
1556 if( ret == 0 )
1557 {
1558 /* In case we just created the double indirect.
1559 */
1560 pInode->pDindir->ulInode = pInode->ulInode;
1561
1562 pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulDindirBlock;
1563 }
1564 }
1565
1566 if( ret == 0 )
1567 #endif /* if DINDIR_POINTERS > 0U */
1568 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1569 {
1570 if( ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) && ( depth >= BRANCHDEPTH_INDIR ) )
1571 {
1572 ret = BranchOneBlock( &pInode->ulIndirBlock, CAST_VOID_PTR_PTR( &pInode->pIndir ), BFLAG_META_INDIR );
1573
1574 if( ret == 0 )
1575 {
1576 /* In case we just created the indirect.
1577 */
1578 pInode->pIndir->ulInode = pInode->ulInode;
1579
1580 #if DINDIR_POINTERS > 0U
1581 if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
1582 {
1583 pInode->pDindir->aulEntries[ pInode->uDindirEntry ] = pInode->ulIndirBlock;
1584 }
1585 else
1586 #endif
1587 {
1588 pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulIndirBlock;
1589 }
1590 }
1591 }
1592 }
1593
1594 if( ret == 0 )
1595 #endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
1596 {
1597 if( depth == BRANCHDEPTH_FILE_DATA )
1598 {
1599 #if REDCONF_INODE_BLOCKS == 1
1600 bool fAllocedNew = ( pInode->ulDataBlock == BLOCK_SPARSE );
1601 #endif
1602 void ** ppBufPtr = ( fBuffer || ( pInode->pbData != NULL ) ) ? CAST_VOID_PTR_PTR( &pInode->pbData ) : NULL;
1603
1604 ret = BranchOneBlock( &pInode->ulDataBlock, ppBufPtr, 0U );
1605
1606 if( ret == 0 )
1607 {
1608 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1609 if( pInode->uIndirEntry != COORD_ENTRY_INVALID )
1610 {
1611 pInode->pIndir->aulEntries[ pInode->uIndirEntry ] = pInode->ulDataBlock;
1612 }
1613 else
1614 #endif
1615 {
1616 pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulDataBlock;
1617 }
1618
1619 #if REDCONF_INODE_BLOCKS == 1
1620 if( fAllocedNew )
1621 {
1622 if( pInode->pInodeBuf->ulBlocks < INODE_DATA_BLOCKS )
1623 {
1624 pInode->pInodeBuf->ulBlocks++;
1625 }
1626 else
1627 {
1628 CRITICAL_ERROR();
1629 ret = -RED_EFUBAR;
1630 }
1631 }
1632 #endif /* if REDCONF_INODE_BLOCKS == 1 */
1633 }
1634 }
1635 }
1636
1637 CRITICAL_ASSERT( ret == 0 );
1638 }
1639
1640 return ret;
1641 }
1642
1643
1644 /** @brief Branch a block.
1645 *
1646 * The block can be a double indirect, indirect, or file data block.
1647 *
1648 * The caller should have already handled the disk full implications of
1649 * branching this block.
1650 *
1651 * @param pulBlock On entry, the current block number, which may be
1652 * BLOCK_SPARSE if the block is to be newly allocated. On
1653 * successful return, populated with the new block number,
1654 * which may be the same as the original block number if it
1655 * was not BLOCK_SPARSE and the block was already branched.
1656 * @param ppBuffer If NULL, indicates that the caller does not want to buffer
1657 * the branched block. If non-NULL, the caller does want the
1658 * branched block buffered, and the following is true: On
1659 * entry, the current buffer for the block, if there is one, or
1660 * NULL if there is no buffer. On successful exit, populated
1661 * with a buffer for the block, which will be dirty. If the
1662 * block number is initially BLOCK_SPARSE, there should be no
1663 * buffer for the block.
1664 * @param uBFlag The buffer type flags: BFLAG_META_DINDIR, BFLAG_META_INDIR,
1665 * or zero for file data.
1666 *
1667 * @retval 0 Operation was successful.
1668 * @retval -RED_EIO A disk I/O error occurred.
1669 * @retval -RED_EINVAL Invalid parameters.
1670 */
BranchOneBlock(uint32_t * pulBlock,void ** ppBuffer,uint16_t uBFlag)1671 static REDSTATUS BranchOneBlock( uint32_t * pulBlock,
1672 void ** ppBuffer,
1673 uint16_t uBFlag )
1674 {
1675 REDSTATUS ret = 0;
1676
1677 if( pulBlock == NULL )
1678 {
1679 REDERROR();
1680 ret = -RED_EINVAL;
1681 }
1682 else
1683 {
1684 ALLOCSTATE state = ALLOCSTATE_FREE;
1685 uint32_t ulPrevBlock = *pulBlock;
1686
1687 if( ulPrevBlock != BLOCK_SPARSE )
1688 {
1689 ret = RedImapBlockState( ulPrevBlock, &state );
1690 }
1691
1692 if( ret == 0 )
1693 {
1694 if( state == ALLOCSTATE_NEW )
1695 {
1696 /* Block is already branched, so simply get it buffered dirty
1697 * if requested.
1698 */
1699 if( ppBuffer != NULL )
1700 {
1701 if( *ppBuffer != NULL )
1702 {
1703 RedBufferDirty( *ppBuffer );
1704 }
1705 else
1706 {
1707 ret = RedBufferGet( ulPrevBlock, uBFlag | BFLAG_DIRTY, ppBuffer );
1708 }
1709 }
1710 }
1711 else
1712 {
1713 /* Block does not exist or is committed state, so allocate a
1714 * new block for the branch.
1715 */
1716 ret = RedImapAllocBlock( pulBlock );
1717
1718 if( ret == 0 )
1719 {
1720 if( ulPrevBlock == BLOCK_SPARSE )
1721 {
1722 /* Block did not exist previously, so just get it
1723 * buffered if requested.
1724 */
1725 if( ppBuffer != NULL )
1726 {
1727 if( *ppBuffer != NULL )
1728 {
1729 /* How could there be an existing buffer when
1730 * the block did not exist?
1731 */
1732 REDERROR();
1733 ret = -RED_EINVAL;
1734 }
1735 else
1736 {
1737 ret = RedBufferGet( *pulBlock, ( uint16_t ) ( ( uint32_t ) uBFlag | BFLAG_NEW | BFLAG_DIRTY ), ppBuffer );
1738 }
1739 }
1740 }
1741 else
1742 {
1743 /* Branch the buffer for the committed state block to
1744 * the newly allocated location.
1745 */
1746 if( ppBuffer != NULL )
1747 {
1748 if( *ppBuffer == NULL )
1749 {
1750 ret = RedBufferGet( ulPrevBlock, uBFlag, ppBuffer );
1751 }
1752
1753 if( ret == 0 )
1754 {
1755 RedBufferBranch( *ppBuffer, *pulBlock );
1756 }
1757 }
1758
1759 /* Mark the committed state block almost free.
1760 */
1761 if( ret == 0 )
1762 {
1763 ret = RedImapBlockSet( ulPrevBlock, false );
1764 }
1765 }
1766 }
1767 }
1768 }
1769 }
1770
1771 return ret;
1772 }
1773
1774
1775 /** @brief Compute the free space cost of branching a block.
1776 *
1777 * The caller must first use RedInodeDataSeek() to the block to be branched.
1778 *
1779 * @param pInode A pointer to the cached inode structure, whose coordinates
1780 * indicate the block to be branched.
1781 * @param depth A BRANCHDEPTH_ value indicating how much of the file
1782 * metadata structure needs to be branched.
1783 * @param pulCost On successful return, populated with the number of blocks
1784 * that must be allocated from free space in order to branch
1785 * the given block.
1786 *
1787 * @return A negated ::REDSTATUS code indicating the operation result.
1788 *
1789 * @retval 0 Operation was successful.
1790 * @retval -RED_EIO A disk I/O error occurred.
1791 * @retval -RED_EINVAL Invalid parameters.
1792 */
BranchBlockCost(const CINODE * pInode,BRANCHDEPTH depth,uint32_t * pulCost)1793 static REDSTATUS BranchBlockCost( const CINODE * pInode,
1794 BRANCHDEPTH depth,
1795 uint32_t * pulCost )
1796 {
1797 REDSTATUS ret = 0;
1798
1799 if( !CINODE_IS_MOUNTED( pInode ) || !pInode->fCoordInited || ( depth > BRANCHDEPTH_MAX ) || ( pulCost == NULL ) )
1800 {
1801 REDERROR();
1802 ret = -RED_EINVAL;
1803 }
1804 else
1805 {
1806 ALLOCSTATE state;
1807
1808 /* ulCost is initialized to the maximum number of blocks that could
1809 * be branched, and decremented for every block we determine does not
1810 * need to be branched.
1811 */
1812 #if DINDIR_POINTERS > 0U
1813 uint32_t ulCost = 3U;
1814 #elif REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1815 uint32_t ulCost = 2U;
1816 #else
1817 uint32_t ulCost = 1U;
1818 #endif
1819
1820 #if DINDIR_POINTERS > 0U
1821 if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
1822 {
1823 if( pInode->ulDindirBlock != BLOCK_SPARSE )
1824 {
1825 ret = RedImapBlockState( pInode->ulDindirBlock, &state );
1826
1827 if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
1828 {
1829 /* Double indirect already branched.
1830 */
1831 ulCost--;
1832 }
1833 }
1834 }
1835 else
1836 {
1837 /* At this inode offset there are no double indirects.
1838 */
1839 ulCost--;
1840 }
1841
1842 if( ret == 0 )
1843 #endif /* if DINDIR_POINTERS > 0U */
1844 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
1845 {
1846 if( ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) && ( depth >= BRANCHDEPTH_INDIR ) )
1847 {
1848 if( pInode->ulIndirBlock != BLOCK_SPARSE )
1849 {
1850 ret = RedImapBlockState( pInode->ulIndirBlock, &state );
1851
1852 if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
1853 {
1854 /* Indirect already branched.
1855 */
1856 ulCost--;
1857 }
1858 }
1859 }
1860 else
1861 {
1862 /* Either not branching this deep, or at this inode offset
1863 * there are no indirects.
1864 */
1865 ulCost--;
1866 }
1867 }
1868
1869 if( ret == 0 )
1870 #endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
1871 {
1872 if( depth == BRANCHDEPTH_FILE_DATA )
1873 {
1874 if( pInode->ulDataBlock != BLOCK_SPARSE )
1875 {
1876 ret = RedImapBlockState( pInode->ulDataBlock, &state );
1877
1878 if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
1879 {
1880 /* File data block already branched.
1881 */
1882 ulCost--;
1883
1884 /* If the file data block is branched, then its parent
1885 * nodes should be branched as well.
1886 */
1887 REDASSERT( ulCost == 0U );
1888 }
1889 }
1890 }
1891 else
1892 {
1893 /* Not branching this deep.
1894 */
1895 ulCost--;
1896 }
1897 }
1898
1899 if( ret == 0 )
1900 {
1901 *pulCost = ulCost;
1902 }
1903 }
1904
1905 return ret;
1906 }
1907
1908
1909 /** @brief Yields the number of currently available free blocks.
1910 *
1911 * Accounts for reserved blocks, subtracting the number of reserved blocks if
1912 * they are unavailable.
1913 *
1914 * @return Number of currently available free blocks.
1915 */
FreeBlockCount(void)1916 static uint32_t FreeBlockCount( void )
1917 {
1918 uint32_t ulFreeBlocks = gpRedMR->ulFreeBlocks;
1919
1920 #if RESERVED_BLOCKS > 0U
1921 if( !gpRedCoreVol->fUseReservedBlocks )
1922 {
1923 if( ulFreeBlocks >= RESERVED_BLOCKS )
1924 {
1925 ulFreeBlocks -= RESERVED_BLOCKS;
1926 }
1927 else
1928 {
1929 ulFreeBlocks = 0U;
1930 }
1931 }
1932 #endif /* if RESERVED_BLOCKS > 0U */
1933
1934 return ulFreeBlocks;
1935 }
1936 #endif /* REDCONF_READ_ONLY == 0 */
1937