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