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 functions.
29 */
30 #include <redfs.h>
31 #include <redcore.h>
32
33
34 #if REDCONF_READ_ONLY == 0
35 static REDSTATUS InodeIsBranched( uint32_t ulInode,
36 bool * pfIsBranched );
37 #endif
38 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
39 static REDSTATUS InodeFindFree( uint32_t * pulInode );
40 #endif
41 #if REDCONF_READ_ONLY == 0
42 static REDSTATUS InodeGetWriteableCopy( uint32_t ulInode,
43 uint8_t * pbWhich );
44 #endif
45 static REDSTATUS InodeGetCurrentCopy( uint32_t ulInode,
46 uint8_t * pbWhich );
47 #if REDCONF_READ_ONLY == 0
48 static REDSTATUS InodeBitSet( uint32_t ulInode,
49 uint8_t bWhich,
50 bool fAllocated );
51 #endif
52 static uint32_t InodeBlock( uint32_t ulInode,
53 uint8_t bWhich );
54
55
56 /** @brief Mount an existing inode.
57 *
58 * Will populate all fields of the cached inode structure, except those which
59 * are populated during seek.
60 *
61 * @param pInode A pointer to the cached inode structure. The
62 * pInode->ulInode field must already be initialized with the
63 * inode number to mount. All other fields will be discarded.
64 * @param type The expected inode type.
65 * @param fBranch Whether to branch the inode.
66 *
67 * @return A negated ::REDSTATUS code indicating the operation result.
68 *
69 * @retval 0 Operation was successful.
70 * @retval -RED_EINVAL Invalid parameters.
71 * @retval -RED_EROFS @p fBranch is true but the driver is read-only.
72 * @retval -RED_EIO A disk I/O error occurred.
73 * @retval -RED_EBADF The inode number is free; or the inode number is not
74 * valid.
75 * @retval -RED_EISDIR @p type is ::FTYPE_FILE and the inode is a directory.
76 * @retval -RED_ENOTDIR @p type is ::FTYPE_DIR and the inode is a file.
77 */
RedInodeMount(CINODE * pInode,FTYPE type,bool fBranch)78 REDSTATUS RedInodeMount( CINODE * pInode,
79 FTYPE type,
80 bool fBranch )
81 {
82 REDSTATUS ret = 0;
83
84 if( pInode == NULL )
85 {
86 ret = -RED_EINVAL;
87 }
88 else if( !INODE_IS_VALID( pInode->ulInode ) )
89 {
90 ret = -RED_EBADF;
91 }
92
93 #if REDCONF_API_FSE == 1
94 else if( type == FTYPE_DIR )
95 {
96 REDERROR();
97 ret = -RED_EINVAL;
98 }
99 #endif
100 #if REDCONF_READ_ONLY == 1
101 else if( fBranch )
102 {
103 REDERROR();
104 ret = -RED_EROFS;
105 }
106 #endif
107 else
108 {
109 uint32_t ulInode = pInode->ulInode;
110 uint8_t bWhich = 0U; /* Init'd to quiet warnings. */
111
112 RedMemSet( pInode, 0U, sizeof( *pInode ) );
113 pInode->ulInode = ulInode;
114
115 ret = InodeGetCurrentCopy( pInode->ulInode, &bWhich );
116
117 if( ret == 0 )
118 {
119 ret = RedBufferGet( InodeBlock( pInode->ulInode, bWhich ), BFLAG_META_INODE, CAST_VOID_PTR_PTR( &pInode->pInodeBuf ) );
120 }
121
122 #if REDCONF_READ_ONLY == 0
123 if( ret == 0 )
124 {
125 ret = InodeIsBranched( pInode->ulInode, &pInode->fBranched );
126 }
127 #endif
128
129 if( ret == 0 )
130 {
131 if( RED_S_ISREG( pInode->pInodeBuf->uMode ) )
132 {
133 #if REDCONF_API_POSIX == 1
134 pInode->fDirectory = false;
135
136 if( type == FTYPE_DIR )
137 {
138 ret = -RED_ENOTDIR;
139 }
140 #endif
141 }
142
143 #if REDCONF_API_POSIX == 1
144 else if( RED_S_ISDIR( pInode->pInodeBuf->uMode ) )
145 {
146 pInode->fDirectory = true;
147
148 if( type == FTYPE_FILE )
149 {
150 ret = -RED_EISDIR;
151 }
152 }
153 #endif
154 else
155 {
156 /* Missing or unsupported inode type.
157 */
158 CRITICAL_ERROR();
159 ret = -RED_EFUBAR;
160 }
161 }
162
163 #if REDCONF_READ_ONLY == 0
164 if( ( ret == 0 ) && fBranch )
165 {
166 ret = RedInodeBranch( pInode );
167 }
168 #endif
169
170 if( ret != 0 )
171 {
172 RedInodePut( pInode, 0U );
173 }
174 }
175
176 return ret;
177 }
178
179
180 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED )
181
182 /** @brief Create an inode.
183 *
184 * @param pInode Pointer to the cached inode structure. If pInode->ulInode
185 * is #INODE_INVALID, a free inode will be found; otherwise,
186 * pInode->ulInode will be the inode number (an error will be
187 * returned if it is not free).
188 * @param ulPInode The parent inode number.
189 * @param uMode The inode mode.
190 *
191 * @return A negated ::REDSTATUS code indicating the operation result.
192 *
193 * @retval 0 Operation was successful.
194 * @retval -RED_EBADF pInode->ulInode is an invalid inode number other than
195 #INODE_INVALID.
196 * @retval -RED_EINVAL Invalid parameters.
197 * @retval -RED_EEXIST Tried to create an inode with an inode number that is
198 * already in use.
199 * @retval -RED_ENFILE All inode slots are already in use.
200 * @retval -RED_EIO A disk I/O error occurred.
201 */
RedInodeCreate(CINODE * pInode,uint32_t ulPInode,uint16_t uMode)202 REDSTATUS RedInodeCreate( CINODE * pInode,
203 uint32_t ulPInode,
204 uint16_t uMode )
205 {
206 REDSTATUS ret;
207
208 #if REDCONF_API_POSIX == 1
209
210 /* ulPInode must be a valid inode number, unless we are creating the root
211 * directory, in which case ulPInode must be INODE_INVALID (the root
212 * directory has no parent).
213 */
214 if( ( pInode == NULL ) ||
215 ( !INODE_IS_VALID( ulPInode ) && ( ( ulPInode != INODE_INVALID ) || ( pInode->ulInode != INODE_ROOTDIR ) ) ) )
216 #else
217 if( pInode == NULL )
218 #endif
219 {
220 REDERROR();
221 ret = -RED_EINVAL;
222 }
223 else
224 {
225 uint32_t ulInode = pInode->ulInode;
226
227 RedMemSet( pInode, 0U, sizeof( *pInode ) );
228
229 #if REDCONF_API_POSIX == 1
230 if( ulInode == INODE_INVALID )
231 {
232 /* Caller requested that an inode number be allocated. Search for
233 * an unused inode number, error if there isn't one.
234 */
235 ret = InodeFindFree( &pInode->ulInode );
236 }
237 else
238 #endif
239 {
240 /* Caller requested creation of a specific inode number. Make sure
241 * it's valid and doesn't already exist.
242 */
243 if( INODE_IS_VALID( ulInode ) )
244 {
245 bool fFree;
246
247 ret = RedInodeIsFree( ulInode, &fFree );
248
249 if( ret == 0 )
250 {
251 if( fFree )
252 {
253 pInode->ulInode = ulInode;
254 }
255 else
256 {
257 ret = -RED_EEXIST;
258 }
259 }
260 }
261 else
262 {
263 ret = -RED_EBADF;
264 }
265 }
266
267 if( ret == 0 )
268 {
269 uint8_t bWriteableWhich;
270
271 ret = InodeGetWriteableCopy( pInode->ulInode, &bWriteableWhich );
272
273 if( ret == 0 )
274 {
275 ret = RedBufferGet( InodeBlock( pInode->ulInode, bWriteableWhich ),
276 ( uint16_t ) ( ( uint32_t ) BFLAG_META_INODE | BFLAG_DIRTY | BFLAG_NEW ), CAST_VOID_PTR_PTR( &pInode->pInodeBuf ) );
277
278 if( ret == 0 )
279 {
280 /* Mark the inode block as allocated.
281 */
282 ret = InodeBitSet( pInode->ulInode, bWriteableWhich, true );
283
284 if( ret != 0 )
285 {
286 RedBufferPut( pInode->pInodeBuf );
287 }
288 }
289 }
290 }
291
292 if( ret == 0 )
293 {
294 #if REDCONF_INODE_TIMESTAMPS == 1
295 uint32_t ulNow = RedOsClockGetTime();
296
297 pInode->pInodeBuf->ulATime = ulNow;
298 pInode->pInodeBuf->ulMTime = ulNow;
299 pInode->pInodeBuf->ulCTime = ulNow;
300 #endif
301
302 pInode->pInodeBuf->uMode = uMode;
303
304 #if REDCONF_API_POSIX == 1
305 #if REDCONF_API_POSIX_LINK == 1
306 pInode->pInodeBuf->uNLink = 1U;
307 #endif
308 pInode->pInodeBuf->ulPInode = ulPInode;
309 #else
310 ( void ) ulPInode;
311 #endif
312
313 pInode->fBranched = true;
314 pInode->fDirty = true;
315
316 #if REDCONF_API_POSIX == 1
317 gpRedMR->ulFreeInodes--;
318 #endif
319 }
320 }
321
322 return ret;
323 }
324 #endif /* (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED) */
325
326
327 #if DELETE_SUPPORTED
328
329 /** @brief Delete an inode.
330 *
331 * @param pInode Pointer to the cached inode structure.
332 *
333 * @return A negated ::REDSTATUS code indicating the operation result.
334 *
335 * @retval 0 Operation was successful.
336 * @retval -RED_EBADF The inode is free.
337 * @retval -RED_EINVAL @p pInode is `NULL`; or pInode->pBuffer is `NULL`.
338 * @retval -RED_EIO A disk I/O error occurred.
339 */
RedInodeDelete(CINODE * pInode)340 REDSTATUS RedInodeDelete( CINODE * pInode )
341 {
342 REDSTATUS ret = 0;
343
344 if( !CINODE_IS_MOUNTED( pInode ) )
345 {
346 ret = -RED_EINVAL;
347 }
348 else
349 {
350 if( pInode->pInodeBuf->ullSize != 0U )
351 {
352 ret = RedInodeDataTruncate( pInode, UINT64_SUFFIX( 0 ) );
353 }
354
355 if( ret == 0 )
356 {
357 ret = RedInodeFree( pInode );
358 }
359 }
360
361 return ret;
362 }
363
364
365 /** @brief Decrement an inode link count and delete the inode if the link count
366 * falls to zero.
367 *
368 * @param pInode A pointer to the cached inode structure.
369 *
370 * @return A negated ::REDSTATUS code indicating the operation result.
371 *
372 * @retval 0 Operation was successful.
373 * @retval -RED_EINVAL @p pInode is not a mounted cached inode.
374 * @retval -RED_EIO A disk I/O error occurred.
375 */
RedInodeLinkDec(CINODE * pInode)376 REDSTATUS RedInodeLinkDec( CINODE * pInode )
377 {
378 REDSTATUS ret;
379
380 if( !CINODE_IS_MOUNTED( pInode ) )
381 {
382 ret = -RED_EINVAL;
383 }
384
385 #if REDCONF_API_POSIX_LINK == 1
386 else if( pInode->pInodeBuf->uNLink > 1U )
387 {
388 ret = RedInodeBranch( pInode );
389
390 if( ret == 0 )
391 {
392 pInode->pInodeBuf->uNLink--;
393 }
394 }
395 #endif
396 else
397 {
398 ret = RedInodeDelete( pInode );
399 }
400
401 return ret;
402 }
403 #endif /* DELETE_SUPPORTED */
404
405
406 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
407
408 /** @brief Free an inode.
409 *
410 * @param pInode Pointer to the cached inode structure.
411 *
412 * @return A negated ::REDSTATUS code indicating the operation result.
413 *
414 * @retval 0 Operation was successful.
415 * @retval -RED_EBADF The inode is free.
416 * @retval -RED_EINVAL @p pInode is `NULL`; or pInode->pBuffer is `NULL`.
417 * @retval -RED_EIO A disk I/O error occurred.
418 */
RedInodeFree(CINODE * pInode)419 REDSTATUS RedInodeFree( CINODE * pInode )
420 {
421 REDSTATUS ret;
422
423 if( !CINODE_IS_MOUNTED( pInode ) )
424 {
425 ret = -RED_EINVAL;
426 }
427 else
428 {
429 bool fSlot0Allocated;
430
431 RedBufferDiscard( pInode->pInodeBuf );
432 pInode->pInodeBuf = NULL;
433
434 /* Determine which of the two slots for the inode is currently
435 * allocated, and free that slot.
436 */
437 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, pInode->ulInode, 0U, &fSlot0Allocated );
438
439 if( ret == 0 )
440 {
441 bool fSlot1Allocated;
442
443 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, pInode->ulInode, 1U, &fSlot1Allocated );
444
445 if( ret == 0 )
446 {
447 if( fSlot0Allocated )
448 {
449 if( fSlot1Allocated )
450 {
451 /* Both inode slots should never be allocated at
452 * the same time.
453 */
454 CRITICAL_ERROR();
455 ret = -RED_EFUBAR;
456 }
457 else
458 {
459 ret = InodeBitSet( pInode->ulInode, 0U, false );
460 }
461 }
462 else
463 {
464 if( !fSlot1Allocated )
465 {
466 /* The inode in unallocated, which should have been
467 * caught when it was mounted.
468 */
469 CRITICAL_ERROR();
470 ret = -RED_EBADF;
471 }
472 else
473 {
474 ret = InodeBitSet( pInode->ulInode, 1U, false );
475 }
476 }
477 }
478 }
479
480 pInode->ulInode = INODE_INVALID;
481
482 if( ret == 0 )
483 {
484 if( gpRedMR->ulFreeInodes >= gpRedVolConf->ulInodeCount )
485 {
486 CRITICAL_ERROR();
487 ret = -RED_EFUBAR;
488 }
489 else
490 {
491 gpRedMR->ulFreeInodes++;
492 }
493 }
494 }
495
496 return ret;
497 }
498 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) */
499
500
501 /** @brief Put the cached inode structure.
502 *
503 * This puts all of the buffers in the ::CINODE structure. Also updates inode
504 * timestamp fields if requested.
505 *
506 * @param pInode The cached inode structure.
507 * @param bTimeFields The inode timestamp fields to update.
508 */
RedInodePut(CINODE * pInode,uint8_t bTimeFields)509 void RedInodePut( CINODE * pInode,
510 uint8_t bTimeFields )
511 {
512 if( pInode == NULL )
513 {
514 REDERROR();
515 }
516 else
517 {
518 RedInodePutCoord( pInode );
519
520 if( pInode->pInodeBuf != NULL )
521 {
522 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 )
523 if( ( bTimeFields & IPUT_UPDATE_MASK ) != 0U )
524 {
525 if( !pInode->fBranched || !pInode->fDirty )
526 {
527 REDERROR();
528 }
529 else
530 {
531 uint32_t ulNow = RedOsClockGetTime();
532
533 #if REDCONF_ATIME == 1
534 if( ( bTimeFields & IPUT_UPDATE_ATIME ) != 0U )
535 {
536 pInode->pInodeBuf->ulATime = ulNow;
537 }
538 #endif
539
540 if( ( bTimeFields & IPUT_UPDATE_MTIME ) != 0U )
541 {
542 pInode->pInodeBuf->ulMTime = ulNow;
543 }
544
545 if( ( bTimeFields & IPUT_UPDATE_CTIME ) != 0U )
546 {
547 pInode->pInodeBuf->ulCTime = ulNow;
548 }
549 }
550 }
551 #else /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 ) */
552 ( void ) bTimeFields;
553 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 ) */
554
555 RedBufferPut( pInode->pInodeBuf );
556 pInode->pInodeBuf = NULL;
557 }
558 }
559 }
560
561
562 /** @brief Put all buffers in the cached inode structure except for the inode
563 * node buffer.
564 *
565 * @param pInode A pointer to the cached inode structure.
566 */
RedInodePutCoord(CINODE * pInode)567 void RedInodePutCoord( CINODE * pInode )
568 {
569 if( pInode == NULL )
570 {
571 REDERROR();
572 }
573 else
574 {
575 RedInodePutData( pInode );
576 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
577 RedInodePutIndir( pInode );
578 #endif
579 #if DINDIR_POINTERS > 0U
580 RedInodePutDindir( pInode );
581 #endif
582 }
583 }
584
585
586 #if DINDIR_POINTERS > 0U
587
588 /** @brief Put the double indirect buffer.
589 *
590 * @param pInode A pointer to the cached inode structure.
591 */
RedInodePutDindir(CINODE * pInode)592 void RedInodePutDindir( CINODE * pInode )
593 {
594 if( pInode == NULL )
595 {
596 REDERROR();
597 }
598 else if( pInode->pDindir != NULL )
599 {
600 RedBufferPut( pInode->pDindir );
601 pInode->pDindir = NULL;
602 }
603 else
604 {
605 /* No double indirect buffer, nothing to put.
606 */
607 }
608 }
609 #endif /* if DINDIR_POINTERS > 0U */
610
611
612 #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
613
614 /** @brief Put the indirect buffer.
615 *
616 * @param pInode A pointer to the cached inode structure.
617 */
RedInodePutIndir(CINODE * pInode)618 void RedInodePutIndir( CINODE * pInode )
619 {
620 if( pInode == NULL )
621 {
622 REDERROR();
623 }
624 else if( pInode->pIndir != NULL )
625 {
626 RedBufferPut( pInode->pIndir );
627 pInode->pIndir = NULL;
628 }
629 else
630 {
631 /* No indirect buffer, nothing to put.
632 */
633 }
634 }
635 #endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
636
637
638 /** @brief Put the inode data buffer.
639 *
640 * @param pInode A pointer to the cached inode structure.
641 */
RedInodePutData(CINODE * pInode)642 void RedInodePutData( CINODE * pInode )
643 {
644 if( pInode == NULL )
645 {
646 REDERROR();
647 }
648 else if( pInode->pbData != NULL )
649 {
650 RedBufferPut( pInode->pbData );
651 pInode->pbData = NULL;
652 }
653 else
654 {
655 /* No data buffer, nothing to put.
656 */
657 }
658 }
659
660
661 #if REDCONF_READ_ONLY == 0
662
663 /** @brief Determine if an inode is branched.
664 *
665 * @param ulInode The inode number to examine.
666 * @param pfIsBranched On successful return, populated with whether the inode
667 * is branched.
668 *
669 * @return A negated ::REDSTATUS code indicating the operation result.
670 *
671 * @retval 0 Operation was successful.
672 * @retval -RED_EINVAL @p pInode is `NULL`; or @p ulInode is not a valid inode
673 * number.
674 * @retval -RED_EIO A disk I/O error occurred.
675 */
InodeIsBranched(uint32_t ulInode,bool * pfIsBranched)676 static REDSTATUS InodeIsBranched( uint32_t ulInode,
677 bool * pfIsBranched )
678 {
679 REDSTATUS ret;
680
681 if( !INODE_IS_VALID( ulInode ) || ( pfIsBranched == NULL ) )
682 {
683 REDERROR();
684 ret = -RED_EINVAL;
685 }
686 else
687 {
688 ALLOCSTATE state;
689
690 ret = RedImapBlockState( InodeBlock( ulInode, 0U ), &state );
691
692 if( ret == 0 )
693 {
694 if( state == ALLOCSTATE_NEW )
695 {
696 *pfIsBranched = true;
697 }
698 else
699 {
700 ret = RedImapBlockState( InodeBlock( ulInode, 1U ), &state );
701
702 if( ret == 0 )
703 {
704 if( state == ALLOCSTATE_NEW )
705 {
706 *pfIsBranched = true;
707 }
708 else
709 {
710 *pfIsBranched = false;
711 }
712 }
713 }
714 }
715 }
716
717 return ret;
718 }
719
720
721 /** @brief Branch an inode.
722 *
723 * A branched inode is one in which the allocation state for one copy is free
724 * or almost free, and the other copy is in the new state. The copy which is
725 * in the new state is the writeable copy, which is also buffered and dirty.
726 *
727 * @param pInode Pointer to the cached inode structure which has already been
728 * mounted.
729 *
730 * @return A negated ::REDSTATUS code indicating the operation result.
731 *
732 * @retval 0 Operation was successful.
733 * @retval -RED_EINVAL Invalid parameters.
734 * @retval -RED_EIO A disk I/O error occurred.
735 */
RedInodeBranch(CINODE * pInode)736 REDSTATUS RedInodeBranch( CINODE * pInode )
737 {
738 REDSTATUS ret;
739
740 if( !CINODE_IS_MOUNTED( pInode ) )
741 {
742 REDERROR();
743 ret = -RED_EINVAL;
744 }
745 else if( !pInode->fBranched )
746 {
747 uint8_t bWhich;
748
749 ret = InodeGetWriteableCopy( pInode->ulInode, &bWhich );
750
751 if( ret == 0 )
752 {
753 RedBufferBranch( pInode->pInodeBuf, InodeBlock( pInode->ulInode, bWhich ) );
754 pInode->fBranched = true;
755 pInode->fDirty = true;
756 }
757
758 /* Toggle the inode slots: the old slot block becomes almost free
759 * (still used by the committed state) and the new slot block becomes
760 * new.
761 */
762 if( ret == 0 )
763 {
764 ret = InodeBitSet( pInode->ulInode, 1U - bWhich, false );
765 }
766
767 if( ret == 0 )
768 {
769 ret = InodeBitSet( pInode->ulInode, bWhich, true );
770 }
771
772 CRITICAL_ASSERT( ret == 0 );
773 }
774 else
775 {
776 RedBufferDirty( pInode->pInodeBuf );
777 pInode->fDirty = true;
778 ret = 0;
779 }
780
781 return ret;
782 }
783 #endif /* REDCONF_READ_ONLY == 0 */
784
785
786 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
787
788 /** @brief Find a free inode number.
789 *
790 * @param pulInode On successful return, populated with a free inode number.
791 *
792 * @return A negated ::REDSTATUS code indicating the operation result.
793 *
794 * @retval 0 Operation was successful.
795 * @retval -RED_EINVAL @p pulInode is `NULL`.
796 * @retval -RED_EIO A disk I/O error occurred.
797 * @retval -RED_ENFILE No available inode numbers.
798 */
InodeFindFree(uint32_t * pulInode)799 static REDSTATUS InodeFindFree( uint32_t * pulInode )
800 {
801 REDSTATUS ret;
802
803 if( pulInode == NULL )
804 {
805 REDERROR();
806 ret = -RED_EINVAL;
807 }
808 else if( gpRedMR->ulFreeInodes == 0U )
809 {
810 ret = -RED_ENFILE;
811 }
812 else
813 {
814 uint32_t ulInode;
815
816 ret = 0;
817
818 for( ulInode = INODE_FIRST_FREE; ulInode < ( INODE_FIRST_VALID + gpRedVolConf->ulInodeCount ); ulInode++ )
819 {
820 bool fFree;
821
822 ret = RedInodeIsFree( ulInode, &fFree );
823
824 if( ( ret != 0 ) || fFree )
825 {
826 break;
827 }
828 }
829
830 if( ret == 0 )
831 {
832 if( ulInode < ( INODE_FIRST_VALID + gpRedVolConf->ulInodeCount ) )
833 {
834 *pulInode = ulInode;
835 }
836 else
837 {
838 /* If gpRedMR->ulFreeInodes > 0, we should have found an inode.
839 */
840 CRITICAL_ERROR();
841 ret = -RED_ENFILE;
842 }
843 }
844 }
845
846 return ret;
847 }
848 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) */
849
850
851 #if ( ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED ) ) || ( REDCONF_CHECKER == 1 )
852
853 /** @brief Determine whether an inode number is available.
854 *
855 * @param ulInode The node number to examine.
856 * @param pfFree On successful return, populated with whether the inode
857 * number is available (true) or in use (false).
858 *
859 * @return A negated ::REDSTATUS code indicating the operation result.
860 *
861 * @retval 0 Operation was successful.
862 * @retval -RED_EINVAL @p pfFree is `NULL`; or @p ulInode is not a valid inode
863 * number.
864 * @retval -RED_EIO A disk I/O error occurred.
865 */
RedInodeIsFree(uint32_t ulInode,bool * pfFree)866 REDSTATUS RedInodeIsFree( uint32_t ulInode,
867 bool * pfFree )
868 {
869 REDSTATUS ret;
870
871 if( pfFree == NULL )
872 {
873 REDERROR();
874 ret = -RED_EINVAL;
875 }
876 else
877 {
878 bool fSlot0Allocated;
879
880 *pfFree = false;
881
882 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
883
884 if( ( ret == 0 ) && !fSlot0Allocated )
885 {
886 bool fSlot1Allocated;
887
888 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
889
890 if( ( ret == 0 ) && !fSlot1Allocated )
891 {
892 *pfFree = true;
893 }
894 }
895 }
896
897 return ret;
898 }
899 #endif /* if ( ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED ) ) || ( REDCONF_CHECKER == 1 ) */
900
901
902 #if REDCONF_READ_ONLY == 0
903
904 /** @brief Determine which copy of the inode is currently writeable.
905 *
906 * @param ulInode The inode number to examine.
907 * @param pbWhich On successful return, populated with which copy of the inode
908 * (either 0 or 1) is writeable.
909 *
910 * @return A negated ::REDSTATUS code indicating the operation result.
911 *
912 * @retval 0 Operation was successful.
913 * @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode
914 * number.
915 * @retval -RED_EIO A disk I/O error occurred.
916 */
InodeGetWriteableCopy(uint32_t ulInode,uint8_t * pbWhich)917 static REDSTATUS InodeGetWriteableCopy( uint32_t ulInode,
918 uint8_t * pbWhich )
919 {
920 REDSTATUS ret;
921
922 if( pbWhich == NULL )
923 {
924 REDERROR();
925 ret = -RED_EINVAL;
926 }
927 else
928 {
929 bool fSlot0Allocated;
930
931 /* The writeable inode slot is the one which is free in the committed
932 * state, so query the committed state metaroot.
933 */
934 ret = RedInodeBitGet( 1U - gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
935
936 if( ret == 0 )
937 {
938 if( !fSlot0Allocated )
939 {
940 *pbWhich = 0U;
941 }
942 else
943 {
944 bool fSlot1Allocated;
945
946 ret = RedInodeBitGet( 1U - gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
947
948 if( ret == 0 )
949 {
950 if( !fSlot1Allocated )
951 {
952 *pbWhich = 1U;
953 }
954 else
955 {
956 /* Both inode slots were allocated, which should never
957 * happen.
958 */
959 CRITICAL_ERROR();
960 ret = -RED_EFUBAR;
961 }
962 }
963 }
964 }
965 }
966
967 return ret;
968 }
969 #endif /* if REDCONF_READ_ONLY == 0 */
970
971
972 /** @brief Determine which copy of the inode is current.
973 *
974 * @param ulInode The inode number to examine.
975 * @param pbWhich On successful return, populated with which copy of the inode
976 * (either 0 or 1) is current.
977 *
978 * @return A negated ::REDSTATUS code indicating the operation result.
979 *
980 * @retval 0 Operation was successful.
981 * @retval -RED_EBADF @p ulInode is an unallocated inode number.
982 * @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode
983 * number.
984 * @retval -RED_EIO A disk I/O error occurred.
985 */
InodeGetCurrentCopy(uint32_t ulInode,uint8_t * pbWhich)986 static REDSTATUS InodeGetCurrentCopy( uint32_t ulInode,
987 uint8_t * pbWhich )
988 {
989 REDSTATUS ret;
990
991 if( pbWhich == NULL )
992 {
993 REDERROR();
994 ret = -RED_EINVAL;
995 }
996 else
997 {
998 bool fSlot0Allocated;
999
1000 /* The current inode slot is the one which is allocated in the working
1001 * state metaroot.
1002 */
1003 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
1004
1005 if( ret == 0 )
1006 {
1007 if( fSlot0Allocated )
1008 {
1009 *pbWhich = 0U;
1010 }
1011 else
1012 {
1013 bool fSlot1Allocated;
1014
1015 ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
1016
1017 if( ret == 0 )
1018 {
1019 if( fSlot1Allocated )
1020 {
1021 *pbWhich = 1U;
1022 }
1023 else
1024 {
1025 /* Neither slot for this inode was allocated, so the
1026 * inode is actually free.
1027 */
1028 ret = -RED_EBADF;
1029 }
1030 }
1031 }
1032 }
1033 }
1034
1035 return ret;
1036 }
1037
1038
1039 /** @brief Get whether a copy of an inode is allocated.
1040 *
1041 * @param bMR The metaroot index: either 0 or 1.
1042 * @param ulInode The inode number.
1043 * @param bWhich Which copy of the inode to get.
1044 * @param pfAllocated On successful return, populated with whether the given
1045 * copy of the inode is allocated.
1046 *
1047 * @return A negated ::REDSTATUS code indicating the operation result.
1048 *
1049 * @retval 0 Operation was successful.
1050 * @retval -RED_EINVAL @p bMR is not 1 or 0; @p ulInode is not a valid inode
1051 * number; or @p bWhich is not 1 or 0; or @p pfAllocated is
1052 * `NULL`.
1053 * @retval -RED_EIO A disk I/O error occurred.
1054 */
RedInodeBitGet(uint8_t bMR,uint32_t ulInode,uint8_t bWhich,bool * pfAllocated)1055 REDSTATUS RedInodeBitGet( uint8_t bMR,
1056 uint32_t ulInode,
1057 uint8_t bWhich,
1058 bool * pfAllocated )
1059 {
1060 REDSTATUS ret;
1061
1062 if( !INODE_IS_VALID( ulInode ) || ( bWhich > 1U ) )
1063 {
1064 REDERROR();
1065 ret = -RED_EINVAL;
1066 }
1067 else
1068 {
1069 ret = RedImapBlockGet( bMR, InodeBlock( ulInode, bWhich ), pfAllocated );
1070 }
1071
1072 return ret;
1073 }
1074
1075
1076 #if REDCONF_READ_ONLY == 0
1077
1078 /** @brief Set whether a copy of an inode is allocated.
1079 *
1080 * @param ulInode The inode number.
1081 * @param bWhich Which copy of the inode to set.
1082 * @param fAllocated If true, the inode is set to allocated; if false, the
1083 * inode is set to free.
1084 *
1085 * @return A negated ::REDSTATUS code indicating the operation result.
1086 *
1087 * @retval 0 Operation was successful.
1088 * @retval -RED_EINVAL @p ulInode is not a valid inode number; or @p bWhich is
1089 * not 1 or 0.
1090 * @retval -RED_EIO A disk I/O error occurred.
1091 */
InodeBitSet(uint32_t ulInode,uint8_t bWhich,bool fAllocated)1092 static REDSTATUS InodeBitSet( uint32_t ulInode,
1093 uint8_t bWhich,
1094 bool fAllocated )
1095 {
1096 REDSTATUS ret;
1097
1098 if( !INODE_IS_VALID( ulInode ) || ( bWhich > 1U ) )
1099 {
1100 REDERROR();
1101 ret = -RED_EINVAL;
1102 }
1103 else
1104 {
1105 ret = RedImapBlockSet( InodeBlock( ulInode, bWhich ), fAllocated );
1106 }
1107
1108 return ret;
1109 }
1110 #endif /* if REDCONF_READ_ONLY == 0 */
1111
1112
1113 /** @brief Determine the block number of an inode.
1114 *
1115 * @param ulInode The inode number.
1116 * @param bWhich Which copy of the inode.
1117 *
1118 * @return The block number of the inode.
1119 */
InodeBlock(uint32_t ulInode,uint8_t bWhich)1120 static uint32_t InodeBlock( uint32_t ulInode,
1121 uint8_t bWhich )
1122 {
1123 REDASSERT( INODE_IS_VALID( ulInode ) );
1124 REDASSERT( bWhich <= 1U );
1125
1126 return gpRedCoreVol->ulInodeTableStartBN + ( ( ulInode - INODE_FIRST_VALID ) * 2U ) + bWhich;
1127 }
1128