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 the entry-points to the core file system.
29  */
30 #include <redfs.h>
31 #include <redcoreapi.h>
32 #include <redcore.h>
33 
34 
35 /*  Minimum number of blocks needed for metadata on any volume: the master
36  *  block (1), the two metaroots (2), and one doubly-allocated inode (2),
37  *  resulting in 1 + 2 + 2 = 5.
38  */
39 #define MINIMUM_METADATA_BLOCKS    ( 5U )
40 
41 
42 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
43     static REDSTATUS CoreCreate( uint32_t ulPInode,
44                                  const char * pszName,
45                                  bool fDir,
46                                  uint32_t * pulInode );
47 #endif
48 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_LINK == 1 )
49     static REDSTATUS CoreLink( uint32_t ulPInode,
50                                const char * pszName,
51                                uint32_t ulInode );
52 #endif
53 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
54     static REDSTATUS CoreUnlink( uint32_t ulPInode,
55                                  const char * pszName );
56 #endif
57 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_RENAME == 1 )
58     static REDSTATUS CoreRename( uint32_t ulSrcPInode,
59                                  const char * pszSrcName,
60                                  uint32_t ulDstPInode,
61                                  const char * pszDstName );
62 #endif
63 #if REDCONF_READ_ONLY == 0
64     static REDSTATUS CoreFileWrite( uint32_t ulInode,
65                                     uint64_t ullStart,
66                                     uint32_t * pulLen,
67                                     const void * pBuffer );
68 #endif
69 #if TRUNCATE_SUPPORTED
70     static REDSTATUS CoreFileTruncate( uint32_t ulInode,
71                                        uint64_t ullSize );
72 #endif
73 
74 
75 VOLUME gaRedVolume[ REDCONF_VOLUME_COUNT ];
76 static COREVOLUME gaCoreVol[ REDCONF_VOLUME_COUNT ];
77 
78 const VOLCONF * CONST_IF_ONE_VOLUME gpRedVolConf = &gaRedVolConf[ 0U ];
79 VOLUME * CONST_IF_ONE_VOLUME gpRedVolume = &gaRedVolume[ 0U ];
80 COREVOLUME * CONST_IF_ONE_VOLUME gpRedCoreVol = &gaCoreVol[ 0U ];
81 METAROOT * gpRedMR = &gaCoreVol[ 0U ].aMR[ 0U ];
82 
83 CONST_IF_ONE_VOLUME uint8_t gbRedVolNum = 0;
84 
85 
86 /** @brief Initialize the Reliance Edge file system driver.
87  *
88  *  Prepares the Reliance Edge file system driver to be used.  Must be the first
89  *  Reliance Edge function to be invoked: no volumes can be mounted until the
90  *  driver has been initialized.
91  *
92  *  If this function is called when the Reliance Edge driver is already
93  *  initialized, the behavior is undefined.
94  *
95  *  @return A negated ::REDSTATUS code indicating the operation result.
96  *
97  *  @retval 0   Operation was successful.
98  */
RedCoreInit(void)99 REDSTATUS RedCoreInit( void )
100 {
101     REDSTATUS ret = 0;
102     uint8_t bVolNum;
103 
104     #if REDCONF_OUTPUT == 1
105         static uint8_t bSignedOn = 0U; /* Whether the sign on has been printed. */
106 
107         if( bSignedOn == 0U )
108         {
109             RedSignOn();
110             bSignedOn = 1U;
111         }
112     #else
113 
114         /*  Call RedSignOn() even when output is disabled, to force the copyright
115          *  text to be referenced and pulled into the program data.
116          */
117         RedSignOn();
118     #endif
119 
120     RedMemSet( gaRedVolume, 0U, sizeof( gaRedVolume ) );
121     RedMemSet( gaCoreVol, 0U, sizeof( gaCoreVol ) );
122 
123     RedBufferInit();
124 
125     for( bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++ )
126     {
127         VOLUME * pVol = &gaRedVolume[ bVolNum ];
128         COREVOLUME * pCoreVol = &gaCoreVol[ bVolNum ];
129         const VOLCONF * pVolConf = &gaRedVolConf[ bVolNum ];
130 
131         if( ( pVolConf->ulSectorSize < SECTOR_SIZE_MIN ) ||
132             ( ( REDCONF_BLOCK_SIZE % pVolConf->ulSectorSize ) != 0U ) ||
133             ( pVolConf->ulInodeCount == 0U ) )
134         {
135             ret = -RED_EINVAL;
136         }
137 
138         #if REDCONF_API_POSIX == 1
139             else if( pVolConf->pszPathPrefix == NULL )
140             {
141                 ret = -RED_EINVAL;
142             }
143             else
144             {
145                 #if REDCONF_VOLUME_COUNT > 1U
146                     uint8_t bCmpVol;
147 
148                     /*  Ensure there are no duplicate path prefixes.  Check against all
149                      *  previous volumes, which are already verified.
150                      */
151                     for( bCmpVol = 0U; bCmpVol < bVolNum; bCmpVol++ )
152                     {
153                         const char * pszCmpPathPrefix = gaRedVolConf[ bCmpVol ].pszPathPrefix;
154 
155                         if( RedStrCmp( pVolConf->pszPathPrefix, pszCmpPathPrefix ) == 0 )
156                         {
157                             ret = -RED_EINVAL;
158                             break;
159                         }
160                     }
161                 #endif /* if REDCONF_VOLUME_COUNT > 1U */
162             }
163         #endif /* if REDCONF_API_POSIX == 1 */
164 
165         if( ret == 0 )
166         {
167             pVol->bBlockSectorShift = 0U;
168 
169             while( ( pVolConf->ulSectorSize << pVol->bBlockSectorShift ) < REDCONF_BLOCK_SIZE )
170             {
171                 pVol->bBlockSectorShift++;
172             }
173 
174             /*  This should always be true since the block size is confirmed to
175              *  be a power of two (checked at compile time) and above we ensured
176              *  that (REDCONF_BLOCK_SIZE % pVolConf->ulSectorSize) == 0.
177              */
178             REDASSERT( ( pVolConf->ulSectorSize << pVol->bBlockSectorShift ) == REDCONF_BLOCK_SIZE );
179 
180             pVol->ulBlockCount = ( uint32_t ) ( pVolConf->ullSectorCount >> pVol->bBlockSectorShift );
181 
182             if( pVol->ulBlockCount < MINIMUM_METADATA_BLOCKS )
183             {
184                 ret = -RED_EINVAL;
185             }
186             else
187             {
188                 #if REDCONF_READ_ONLY == 0
189                     pVol->ulTransMask = REDCONF_TRANSACT_DEFAULT;
190                 #endif
191 
192                 pVol->ullMaxInodeSize = INODE_SIZE_MAX;
193 
194                 /*  To understand the following code, note that the fixed-
195                  *  location metadata is located at the start of the disk, in
196                  *  the following order:
197                  *
198                  *  - Master block (1 block)
199                  *  - Metaroots (2 blocks)
200                  *  - External imap blocks (variable * 2 blocks)
201                  *  - Inode blocks (pVolConf->ulInodeCount * 2 blocks)
202                  */
203 
204                 /*  The imap needs bits for all inode and allocable blocks.  If
205                  *  that bitmap will fit into the metaroot, the inline imap is
206                  *  used and there are no imap nodes on disk.  The minus 3 is
207                  *  there since the imap does not include bits for the master
208                  *  block or metaroots.
209                  */
210                 pCoreVol->fImapInline = ( pVol->ulBlockCount - 3U ) <= METAROOT_ENTRIES;
211 
212                 if( pCoreVol->fImapInline )
213                 {
214                     #if REDCONF_IMAP_INLINE == 1
215                         pCoreVol->ulInodeTableStartBN = 3U;
216                     #else
217                         ret = -RED_EINVAL;
218                     #endif
219                 }
220                 else
221                 {
222                     #if REDCONF_IMAP_EXTERNAL == 1
223                         pCoreVol->ulImapStartBN = 3U;
224 
225                         /*  The imap does not include bits for itself, so add two to
226                          *  the number of imap entries for the two blocks of each
227                          *  imap node.  This allows us to divide up the remaining
228                          *  space, making sure to round up so all data blocks are
229                          *  covered.
230                          */
231                         pCoreVol->ulImapNodeCount =
232                             ( ( pVol->ulBlockCount - 3U ) + ( ( IMAPNODE_ENTRIES + 2U ) - 1U ) ) / ( IMAPNODE_ENTRIES + 2U );
233 
234                         pCoreVol->ulInodeTableStartBN = pCoreVol->ulImapStartBN + ( pCoreVol->ulImapNodeCount * 2U );
235                     #else
236                         ret = -RED_EINVAL;
237                     #endif
238                 }
239             }
240         }
241 
242         if( ret == 0 )
243         {
244             pCoreVol->ulFirstAllocableBN = pCoreVol->ulInodeTableStartBN + ( pVolConf->ulInodeCount * 2U );
245 
246             if( pCoreVol->ulFirstAllocableBN > pVol->ulBlockCount )
247             {
248                 /*  We can get here if there is not enough space for the number
249                  *  of configured inodes.
250                  */
251                 ret = -RED_EINVAL;
252             }
253             else
254             {
255                 pVol->ulBlocksAllocable = pVol->ulBlockCount - pCoreVol->ulFirstAllocableBN;
256             }
257         }
258 
259         if( ret != 0 )
260         {
261             break;
262         }
263     }
264 
265     /*  Make sure the configured endianness is correct.
266      */
267     if( ret == 0 )
268     {
269         uint16_t uValue = 0xFF00U;
270         uint8_t abBytes[ 2U ];
271 
272         RedMemCpy( abBytes, &uValue, sizeof( abBytes ) );
273 
274         #if REDCONF_ENDIAN_BIG == 1
275             if( abBytes[ 0U ] != 0xFFU )
276         #else
277             if( abBytes[ 0U ] != 0x00U )
278         #endif
279         {
280             ret = -RED_EINVAL;
281         }
282     }
283 
284     if( ret == 0 )
285     {
286         ret = RedOsClockInit();
287 
288         #if REDCONF_TASK_COUNT > 1U
289             if( ret == 0 )
290             {
291                 ret = RedOsMutexInit();
292 
293                 if( ret != 0 )
294                 {
295                     ( void ) RedOsClockUninit();
296                 }
297             }
298         #endif
299     }
300 
301     return ret;
302 }
303 
304 
305 /** @brief Uninitialize the Reliance Edge file system driver.
306  *
307  *  Tears down the Reliance Edge file system driver.  Cannot be used until all
308  *  Reliance Edge volumes are unmounted.  A subsequent call to RedCoreInit()
309  *  will initialize the driver again.
310  *
311  *  The behavior of calling this function when the core is already uninitialized
312  *  is undefined.
313  *
314  *  @return A negated ::REDSTATUS code indicating the operation result.
315  *
316  *  @retval 0           Operation was successful.
317  *  @retval -RED_EBUSY  At least one volume is still mounted.
318  */
RedCoreUninit(void)319 REDSTATUS RedCoreUninit( void )
320 {
321     REDSTATUS ret;
322 
323     #if REDCONF_TASK_COUNT > 1U
324         ret = RedOsMutexUninit();
325 
326         if( ret == 0 )
327     #endif
328     {
329         ret = RedOsClockUninit();
330     }
331 
332     return ret;
333 }
334 
335 
336 /** @brief Set the current volume.
337  *
338  *  All core APIs operate on the current volume.  This call must precede all
339  *  core accesses.
340  *
341  *  @param bVolNum  The volume number to access.
342  *
343  *  @return A negated ::REDSTATUS code indicating the operation result.
344  *
345  *  @retval 0           Operation was successful.
346  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number.
347  */
RedCoreVolSetCurrent(uint8_t bVolNum)348 REDSTATUS RedCoreVolSetCurrent( uint8_t bVolNum )
349 {
350     REDSTATUS ret;
351 
352     if( bVolNum >= REDCONF_VOLUME_COUNT )
353     {
354         ret = -RED_EINVAL;
355     }
356     else
357     {
358         #if REDCONF_VOLUME_COUNT > 1U
359             gbRedVolNum = bVolNum;
360             gpRedVolConf = &gaRedVolConf[ bVolNum ];
361             gpRedVolume = &gaRedVolume[ bVolNum ];
362             gpRedCoreVol = &gaCoreVol[ bVolNum ];
363             gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
364         #endif
365 
366         ret = 0;
367     }
368 
369     return ret;
370 }
371 
372 
373 #if FORMAT_SUPPORTED
374 
375 /** @brief Format a file system volume.
376  *
377  *  Uses the statically defined volume configuration.  After calling this
378  *  function, the volume needs to be mounted -- see RedCoreVolMount().
379  *
380  *  An error is returned if the volume is mounted.
381  *
382  *  @return A negated ::REDSTATUS code indicating the operation result.
383  *
384  *  @retval 0           Operation was successful.
385  *  @retval -RED_EBUSY  Volume is mounted.
386  *  @retval -RED_EIO    A disk I/O error occurred.
387  */
RedCoreVolFormat(void)388     REDSTATUS RedCoreVolFormat( void )
389     {
390         return RedVolFormat();
391     }
392 #endif /* FORMAT_SUPPORTED */
393 
394 
395 /** @brief Mount a file system volume.
396  *
397  *  Prepares the file system volume to be accessed.  Mount will fail if the
398  *  volume has never been formatted, or if the on-disk format is inconsistent
399  *  with the compile-time configuration.
400  *
401  *  If the volume is already mounted, the behavior is undefined.
402  *
403  *  @return A negated ::REDSTATUS code indicating the operation result.
404  *
405  *  @retval 0           Operation was successful.
406  *  @retval -RED_EIO    Volume not formatted, improperly formatted, or corrupt.
407  */
RedCoreVolMount(void)408 REDSTATUS RedCoreVolMount( void )
409 {
410     return RedVolMount();
411 }
412 
413 
414 /** @brief Unmount a file system volume.
415  *
416  *  This function discards the in-memory state for the file system and marks it
417  *  as unmounted.  Subsequent attempts to access the volume will fail until the
418  *  volume is mounted again.
419  *
420  *  If unmount automatic transaction points are enabled, this function will
421  *  commit a transaction point prior to unmounting.  If unmount automatic
422  *  transaction points are disabled, this function will unmount without
423  *  transacting, effectively discarding the working state.
424  *
425  *  If the volume is already unmounted, the behavior is undefined.
426  *
427  *  @return A negated ::REDSTATUS code indicating the operation result.
428  *
429  *  @retval 0           Operation was successful.
430  *  @retval -RED_EIO    I/O error during unmount automatic transaction point.
431  */
RedCoreVolUnmount(void)432 REDSTATUS RedCoreVolUnmount( void )
433 {
434     REDSTATUS ret = 0;
435 
436     #if REDCONF_READ_ONLY == 0
437         if( !gpRedVolume->fReadOnly && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_UMOUNT ) != 0U ) )
438         {
439             ret = RedVolTransact();
440         }
441     #endif
442 
443     if( ret == 0 )
444     {
445         ret = RedBufferDiscardRange( 0U, gpRedVolume->ulBlockCount );
446     }
447 
448     if( ret == 0 )
449     {
450         ret = RedOsBDevClose( gbRedVolNum );
451     }
452 
453     if( ret == 0 )
454     {
455         gpRedVolume->fMounted = false;
456     }
457 
458     return ret;
459 }
460 
461 
462 #if REDCONF_READ_ONLY == 0
463 
464 /** @brief Commit a transaction point.
465  *
466  *  Reliance Edge is a transactional file system.  All modifications, of both
467  *  metadata and filedata, are initially working state.  A transaction point
468  *  is a process whereby the working state atomically becomes the committed
469  *  state, replacing the previous committed state.  Whenever Reliance Edge is
470  *  mounted, including after power loss, the state of the file system after
471  *  mount is the most recent committed state.  Nothing from the committed
472  *  state is ever missing, and nothing from the working state is ever included.
473  *
474  *  @return A negated ::REDSTATUS code indicating the operation result.
475  *
476  *  @retval 0           Operation was successful.
477  *  @retval -RED_EINVAL The volume is not mounted.
478  *  @retval -RED_EIO    A disk I/O error occurred.
479  *  @retval -RED_EROFS  The file system volume is read-only.
480  */
RedCoreVolTransact(void)481     REDSTATUS RedCoreVolTransact( void )
482     {
483         REDSTATUS ret;
484 
485         if( !gpRedVolume->fMounted )
486         {
487             ret = -RED_EINVAL;
488         }
489         else if( gpRedVolume->fReadOnly )
490         {
491             ret = -RED_EROFS;
492         }
493         else
494         {
495             ret = RedVolTransact();
496         }
497 
498         return ret;
499     }
500 #endif /* REDCONF_READ_ONLY == 0 */
501 
502 
503 #if REDCONF_API_POSIX == 1
504 
505 /** @brief Query file system status information.
506  *
507  *  @param pStatFS  The buffer to populate with volume information.
508  *
509  *  @return A negated ::REDSTATUS code indicating the operation result.
510  *
511  *  @retval -RED_EINVAL Volume is not mounted; or @p pStatFS is `NULL`.
512  */
RedCoreVolStat(REDSTATFS * pStatFS)513     REDSTATUS RedCoreVolStat( REDSTATFS * pStatFS )
514     {
515         REDSTATUS ret;
516 
517         if( ( pStatFS == NULL ) || ( !gpRedVolume->fMounted ) )
518         {
519             ret = -RED_EINVAL;
520         }
521         else
522         {
523             RedMemSet( pStatFS, 0U, sizeof( *pStatFS ) );
524 
525             pStatFS->f_bsize = REDCONF_BLOCK_SIZE;
526             pStatFS->f_frsize = REDCONF_BLOCK_SIZE;
527             pStatFS->f_blocks = gpRedVolume->ulBlockCount;
528             #if RESERVED_BLOCKS > 0U
529                 pStatFS->f_bfree = ( gpRedMR->ulFreeBlocks > RESERVED_BLOCKS ) ? ( gpRedMR->ulFreeBlocks - RESERVED_BLOCKS ) : 0U;
530             #else
531                 pStatFS->f_bfree = gpRedMR->ulFreeBlocks;
532             #endif
533             pStatFS->f_bavail = pStatFS->f_bfree;
534             pStatFS->f_files = gpRedVolConf->ulInodeCount;
535             pStatFS->f_ffree = gpRedMR->ulFreeInodes;
536             pStatFS->f_favail = gpRedMR->ulFreeInodes;
537 
538             pStatFS->f_flag = RED_ST_NOSUID;
539             #if REDCONF_READ_ONLY == 0
540                 if( gpRedVolume->fReadOnly )
541             #endif
542             {
543                 pStatFS->f_flag |= RED_ST_RDONLY;
544             }
545 
546             pStatFS->f_namemax = REDCONF_NAME_MAX;
547             pStatFS->f_maxfsize = INODE_SIZE_MAX;
548             pStatFS->f_dev = gbRedVolNum;
549 
550             ret = 0;
551         }
552 
553         return ret;
554     }
555 #endif /* REDCONF_API_POSIX == 1 */
556 
557 
558 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKSET == 1 ) )
559 
560 /** @brief Update the transaction mask.
561  *
562  *  The following events are available when using the FSE API:
563  *
564  *  - #RED_TRANSACT_UMOUNT
565  *  - #RED_TRANSACT_WRITE
566  *  - #RED_TRANSACT_TRUNCATE
567  *  - #RED_TRANSACT_VOLFULL
568  *
569  *  The following events are available when using the POSIX-like API:
570  *
571  *  - #RED_TRANSACT_UMOUNT
572  *  - #RED_TRANSACT_CREAT
573  *  - #RED_TRANSACT_UNLINK
574  *  - #RED_TRANSACT_MKDIR
575  *  - #RED_TRANSACT_RENAME
576  *  - #RED_TRANSACT_LINK
577  *  - #RED_TRANSACT_CLOSE
578  *  - #RED_TRANSACT_WRITE
579  *  - #RED_TRANSACT_FSYNC
580  *  - #RED_TRANSACT_TRUNCATE
581  *  - #RED_TRANSACT_VOLFULL
582  *
583  *  The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all
584  *  automatic transaction events.  The #RED_TRANSACT_MASK macro is a bitmask of
585  *  all transaction flags, excluding those representing excluded functionality.
586  *
587  *  Attempting to enable events for excluded functionality will result in an
588  *  error.
589  *
590  *  @param ulEventMask  A bitwise-OR'd mask of automatic transaction events to
591  *                      be set as the current transaction mode.
592  *
593  *  @return A negated ::REDSTATUS code indicating the operation result.
594  *
595  *  @retval 0           Operation was successful.
596  *  @retval -RED_EINVAL The volume is not mounted; or @p ulEventMask contains
597  *                      invalid bits.
598  *  @retval -RED_EROFS  The file system volume is read-only.
599  */
RedCoreTransMaskSet(uint32_t ulEventMask)600     REDSTATUS RedCoreTransMaskSet( uint32_t ulEventMask )
601     {
602         REDSTATUS ret;
603 
604         if( !gpRedVolume->fMounted || ( ( ulEventMask & RED_TRANSACT_MASK ) != ulEventMask ) )
605         {
606             ret = -RED_EINVAL;
607         }
608         else if( gpRedVolume->fReadOnly )
609         {
610             ret = -RED_EROFS;
611         }
612         else
613         {
614             gpRedVolume->ulTransMask = ulEventMask;
615             ret = 0;
616         }
617 
618         return ret;
619     }
620 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKSET == 1 ) ) */
621 
622 
623 #if ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKGET == 1 )
624 
625 /** @brief Read the transaction mask.
626  *
627  *  If the volume is read-only, the returned event mask is always zero.
628  *
629  *  @param pulEventMask Populated with a bitwise-OR'd mask of automatic
630  *                      transaction events which represent the current
631  *                      transaction mode for the volume.
632  *
633  *  @return A negated ::REDSTATUS code indicating the operation result.
634  *
635  *  @retval 0           Operation was successful.
636  *  @retval -RED_EINVAL The volume is not mounted; or @p pulEventMask is `NULL`.
637  */
RedCoreTransMaskGet(uint32_t * pulEventMask)638     REDSTATUS RedCoreTransMaskGet( uint32_t * pulEventMask )
639     {
640         REDSTATUS ret;
641 
642         if( !gpRedVolume->fMounted || ( pulEventMask == NULL ) )
643         {
644             ret = -RED_EINVAL;
645         }
646         else
647         {
648             #if REDCONF_READ_ONLY == 1
649                 *pulEventMask = 0U;
650             #else
651                 *pulEventMask = gpRedVolume->ulTransMask;
652             #endif
653             ret = 0;
654         }
655 
656         return ret;
657     }
658 #endif /* if ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKGET == 1 ) */
659 
660 
661 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
662 
663 /** @brief Create a file or directory.
664  *
665  *  @param ulPInode The inode number of the parent directory.
666  *  @param pszName  A null-terminated name for the new inode.
667  *  @param fDir     Whether to create a directory (true) or file (false).
668  *  @param pulInode On successful return, populated with the inode number of the
669  *                  new file or directory.
670  *
671  *  @return A negated ::REDSTATUS code indicating the operation result.
672  *
673  *  @retval 0                   Operation was successful.
674  *  @retval -RED_EINVAL         The volume is not mounted; or @p pszName is not
675  *                              a valid name; or @p pulInode is `NULL`.
676  *  @retval -RED_EIO            A disk I/O error occurred.
677  *  @retval -RED_EROFS          The file system volume is read-only.
678  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
679  *  @retval -RED_EBADF          @p ulPInode is not a valid inode.
680  *  @retval -RED_ENOSPC         There is not enough space on the volume to
681  *                              createthe new directory entry; or the directory
682  *                              is full.
683  *  @retval -RED_ENFILE         No available inode slots.
684  *  @retval -RED_ENAMETOOLONG   @p pszName is too long.
685  *  @retval -RED_EEXIST         @p pszName already exists in @p ulPInode.
686  */
RedCoreCreate(uint32_t ulPInode,const char * pszName,bool fDir,uint32_t * pulInode)687     REDSTATUS RedCoreCreate( uint32_t ulPInode,
688                              const char * pszName,
689                              bool fDir,
690                              uint32_t * pulInode )
691     {
692         REDSTATUS ret;
693 
694         if( !gpRedVolume->fMounted )
695         {
696             ret = -RED_EINVAL;
697         }
698         else if( gpRedVolume->fReadOnly )
699         {
700             ret = -RED_EROFS;
701         }
702         else
703         {
704             ret = CoreCreate( ulPInode, pszName, fDir, pulInode );
705 
706             if( ( ret == -RED_ENOSPC ) &&
707                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
708                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
709             {
710                 ret = RedVolTransact();
711 
712                 if( ret == 0 )
713                 {
714                     ret = CoreCreate( ulPInode, pszName, fDir, pulInode );
715                 }
716             }
717 
718             if( ret == 0 )
719             {
720                 if( fDir && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_MKDIR ) != 0U ) )
721                 {
722                     ret = RedVolTransact();
723                 }
724                 else if( !fDir && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_CREAT ) != 0U ) )
725                 {
726                     ret = RedVolTransact();
727                 }
728                 else
729                 {
730                     /*  No automatic transaction for this operation.
731                      */
732                 }
733             }
734         }
735 
736         return ret;
737     }
738 
739 
740 /** @brief Create a file or directory.
741  *
742  *  @param ulPInode The inode number of the parent directory.
743  *  @param pszName  A null-terminated name for the new inode.
744  *  @param fDir     Whether to create a directory (true) or file (false).
745  *  @param pulInode On successful return, populated with the inode number of the
746  *                  new file or directory.
747  *
748  *  @return A negated ::REDSTATUS code indicating the operation result.
749  *
750  *  @retval 0                   Operation was successful.
751  *  @retval -RED_EIO            A disk I/O error occurred.
752  *  @retval -RED_EROFS          The file system volume is read-only.
753  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
754  *  @retval -RED_EBADF          @p ulPInode is not a valid inode.
755  *  @retval -RED_ENOSPC         There is not enough space on the volume to
756  *                              create the new directory entry; or the directory
757  *                              is full.
758  *  @retval -RED_ENFILE         No available inode slots.
759  *  @retval -RED_ENAMETOOLONG   @p pszName is too long.
760  *  @retval -RED_EEXIST         @p pszName already exists in @p ulPInode.
761  */
CoreCreate(uint32_t ulPInode,const char * pszName,bool fDir,uint32_t * pulInode)762     static REDSTATUS CoreCreate( uint32_t ulPInode,
763                                  const char * pszName,
764                                  bool fDir,
765                                  uint32_t * pulInode )
766     {
767         REDSTATUS ret;
768 
769         if( pulInode == NULL )
770         {
771             ret = -RED_EINVAL;
772         }
773         else if( gpRedVolume->fReadOnly )
774         {
775             ret = -RED_EROFS;
776         }
777         else
778         {
779             CINODE pino;
780 
781             pino.ulInode = ulPInode;
782             ret = RedInodeMount( &pino, FTYPE_DIR, false );
783 
784             if( ret == 0 )
785             {
786                 CINODE ino;
787 
788                 ino.ulInode = INODE_INVALID;
789                 ret = RedInodeCreate( &ino, ulPInode, fDir ? RED_S_IFDIR : RED_S_IFREG );
790 
791                 if( ret == 0 )
792                 {
793                     ret = RedInodeBranch( &pino );
794 
795                     if( ret == 0 )
796                     {
797                         ret = RedDirEntryCreate( &pino, pszName, ino.ulInode );
798                     }
799 
800                     if( ret == 0 )
801                     {
802                         *pulInode = ino.ulInode;
803                     }
804                     else
805                     {
806                         REDSTATUS ret2;
807 
808                         ret2 = RedInodeFree( &ino );
809                         CRITICAL_ASSERT( ret2 == 0 );
810                     }
811 
812                     RedInodePut( &ino, 0U );
813                 }
814 
815                 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
816             }
817         }
818 
819         return ret;
820     }
821 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) */
822 
823 
824 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_LINK == 1 )
825 
826 /** @brief Create a hard link.
827  *
828  *  This creates an additional name (link) for @p ulInode.  The new name refers
829  *  to the same file with the same contents.  If a name is deleted, but the
830  *  underlying file has other names, the file continues to exist.  The link
831  *  count (accessible via RedCoreStat()) indicates the number of names that a
832  *  file has.  All of a file's names are on equal footing: there is nothing
833  *  special about the original name.
834  *
835  *  If @p ulInode names a directory, the operation will fail.
836  *
837  *  @param ulPInode The inode number of the parent directory.
838  *  @param pszName  The null-terminated name for the new link.
839  *  @param ulInode  The inode to create a hard link to.
840  *
841  *  @return A negated ::REDSTATUS code indicating the operation result.
842  *
843  *  @retval 0                   Operation was successful.
844  *  @retval -RED_EBADF          @p ulPInode is not a valid inode; or @p ulInode
845  *                              is not a valid inode.
846  *  @retval -RED_EEXIST         @p pszName resolves to an existing file.
847  *  @retval -RED_EINVAL         The volume is not mounted; or @p pszName is
848  *                              `NULL`.
849  *  @retval -RED_EIO            A disk I/O error occurred.
850  *  @retval -RED_EMLINK         Creating the link would exceed the maximum link
851  *                              count of @p ulInode.
852  *  @retval -RED_ENAMETOOLONG   Attempting to create a link with a name that
853  *                              exceeds the maximum name length.
854  *  @retval -RED_ENOSPC         There is insufficient free space to expand the
855  *                              directory that would contain the link.
856  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
857  *  @retval -RED_EPERM          @p ulInode is a directory.
858  *  @retval -RED_EROFS          The requested link requires writing in a
859  *                              directory on a read-only file system.
860  */
RedCoreLink(uint32_t ulPInode,const char * pszName,uint32_t ulInode)861     REDSTATUS RedCoreLink( uint32_t ulPInode,
862                            const char * pszName,
863                            uint32_t ulInode )
864     {
865         REDSTATUS ret;
866 
867         if( !gpRedVolume->fMounted )
868         {
869             ret = -RED_EINVAL;
870         }
871         else if( gpRedVolume->fReadOnly )
872         {
873             ret = -RED_EROFS;
874         }
875         else
876         {
877             ret = CoreLink( ulPInode, pszName, ulInode );
878 
879             if( ( ret == -RED_ENOSPC ) &&
880                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
881                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
882             {
883                 ret = RedVolTransact();
884 
885                 if( ret == 0 )
886                 {
887                     ret = CoreLink( ulPInode, pszName, ulInode );
888                 }
889             }
890 
891             if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_LINK ) != 0U ) )
892             {
893                 ret = RedVolTransact();
894             }
895         }
896 
897         return ret;
898     }
899 
900 
901 /** @brief Create a hard link.
902  *
903  *  @param ulPInode The inode number of the parent directory.
904  *  @param pszName  The null-terminated name for the new link.
905  *  @param ulInode  The inode to create a hard link to.
906  *
907  *  @return A negated ::REDSTATUS code indicating the operation result.
908  *
909  *  @retval 0                   Operation was successful.
910  *  @retval -RED_EBADF          @p ulPInode is not a valid inode; or @p ulInode
911  *                              is not a valid inode.
912  *  @retval -RED_EEXIST         @p pszName resolves to an existing file.
913  *  @retval -RED_EIO            A disk I/O error occurred.
914  *  @retval -RED_EMLINK         Creating the link would exceed the maximum link
915  *                              count of @p ulInode.
916  *  @retval -RED_ENAMETOOLONG   Attempting to create a link with a name that
917  *                              exceeds the maximum name length.
918  *  @retval -RED_ENOSPC         There is insufficient free space to expand the
919  *                              directory that would contain the link.
920  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
921  *  @retval -RED_EPERM          @p ulInode is a directory.
922  *  @retval -RED_EROFS          The requested link requires writing in a
923  *                              directory on a read-only file system.
924  */
CoreLink(uint32_t ulPInode,const char * pszName,uint32_t ulInode)925     static REDSTATUS CoreLink( uint32_t ulPInode,
926                                const char * pszName,
927                                uint32_t ulInode )
928     {
929         REDSTATUS ret;
930 
931         if( gpRedVolume->fReadOnly )
932         {
933             ret = -RED_EROFS;
934         }
935         else
936         {
937             CINODE pino;
938 
939             pino.ulInode = ulPInode;
940             ret = RedInodeMount( &pino, FTYPE_DIR, false );
941 
942             if( ret == 0 )
943             {
944                 CINODE ino;
945 
946                 ino.ulInode = ulInode;
947                 ret = RedInodeMount( &ino, FTYPE_FILE, false );
948 
949                 /*  POSIX specifies EPERM as the errno thrown when link() is given a
950                  *  directory.  Switch the errno returned if EISDIR was the return
951                  *  value.
952                  */
953                 if( ret == -RED_EISDIR )
954                 {
955                     ret = -RED_EPERM;
956                 }
957 
958                 if( ret == 0 )
959                 {
960                     if( ino.pInodeBuf->uNLink == UINT16_MAX )
961                     {
962                         ret = -RED_EMLINK;
963                     }
964                     else
965                     {
966                         ret = RedInodeBranch( &pino );
967                     }
968 
969                     if( ret == 0 )
970                     {
971                         ret = RedInodeBranch( &ino );
972                     }
973 
974                     if( ret == 0 )
975                     {
976                         ret = RedDirEntryCreate( &pino, pszName, ino.ulInode );
977                     }
978 
979                     if( ret == 0 )
980                     {
981                         ino.pInodeBuf->uNLink++;
982                     }
983 
984                     RedInodePut( &ino, ( ret == 0 ) ? IPUT_UPDATE_CTIME : 0U );
985                 }
986 
987                 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
988             }
989         }
990 
991         return ret;
992     }
993 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1) */
994 
995 
996 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
997 
998 /** @brief Delete a file or directory.
999  *
1000  *  The given name is deleted and the link count of the corresponding inode is
1001  *  decremented.  If the link count falls to zero (no remaining hard links),
1002  *  the inode will be deleted.
1003  *
1004  *  If the path names a directory which is not empty, the unlink will fail.
1005  *
1006  *  If the deletion frees data in the committed state, it will not return to
1007  *  free space until after a transaction point.  Similarly, if the inode was
1008  *  part of the committed state, the inode slot will not be available until
1009  *  after a transaction point.
1010  *
1011  *  This function can fail when the disk is full.  To fix this, transact and
1012  *  try again: Reliance Edge guarantees that it is possible to delete at least
1013  *  one file or directory after a transaction point.  If disk full automatic
1014  *  transactions are enabled, this will happen automatically.
1015  *
1016  *  @param ulPInode The inode number of the parent directory.
1017  *  @param pszName  The null-terminated name of the file or directory to
1018  *                  delete.
1019  *
1020  *  @return A negated ::REDSTATUS code indicating the operation result.
1021  *
1022  *  @retval 0                   Operation was successful.
1023  *  @retval -RED_EBADF          @p ulPInode is not a valid inode.
1024  *  @retval -RED_EINVAL         The volume is not mounted; or @p pszName is
1025  *                              `NULL`.
1026  *  @retval -RED_EIO            A disk I/O error occurred.
1027  *  @retval -RED_ENAMETOOLONG   @p pszName is too long.
1028  *  @retval -RED_ENOENT         @p pszName does not name an existing file or
1029  *                              directory.
1030  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
1031  *  @retval -RED_ENOSPC         The file system does not have enough space to
1032  *                              modify the parent directory to perform the
1033  *                              deletion.
1034  *  @retval -RED_ENOTEMPTY      The inode referred to by @p pszName is a
1035  *                              directory which is not empty.
1036  */
RedCoreUnlink(uint32_t ulPInode,const char * pszName)1037     REDSTATUS RedCoreUnlink( uint32_t ulPInode,
1038                              const char * pszName )
1039     {
1040         REDSTATUS ret;
1041 
1042         if( !gpRedVolume->fMounted )
1043         {
1044             ret = -RED_EINVAL;
1045         }
1046         else if( gpRedVolume->fReadOnly )
1047         {
1048             ret = -RED_EROFS;
1049         }
1050         else
1051         {
1052             ret = CoreUnlink( ulPInode, pszName );
1053 
1054             if( ( ret == -RED_ENOSPC ) &&
1055                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1056                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1057             {
1058                 ret = RedVolTransact();
1059 
1060                 if( ret == 0 )
1061                 {
1062                     ret = CoreUnlink( ulPInode, pszName );
1063                 }
1064             }
1065 
1066             if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_UNLINK ) != 0U ) )
1067             {
1068                 ret = RedVolTransact();
1069             }
1070         }
1071 
1072         return ret;
1073     }
1074 
1075 
1076 /** @brief Delete a file or directory.
1077  *
1078  *  @param ulPInode The inode number of the parent directory.
1079  *  @param pszName  The null-terminated name of the file or directory to
1080  *                  delete.
1081  *
1082  *  @return A negated ::REDSTATUS code indicating the operation result.
1083  *
1084  *  @retval 0                   Operation was successful.
1085  *  @retval -RED_EBADF          @p ulPInode is not a valid inode.
1086  *  @retval -RED_EIO            A disk I/O error occurred.
1087  *  @retval -RED_ENAMETOOLONG   @p pszName is too long.
1088  *  @retval -RED_ENOENT         @p pszName does not name an existing file or
1089  *                              directory.
1090  *  @retval -RED_ENOTDIR        @p ulPInode is not a directory.
1091  *  @retval -RED_ENOSPC         The file system does not have enough space to
1092  *                              modify the parent directory to perform the
1093  *                              deletion.
1094  *  @retval -RED_ENOTEMPTY      The inode referred to by @p pszName is a
1095  *                              directory which is not empty.
1096  */
CoreUnlink(uint32_t ulPInode,const char * pszName)1097     static REDSTATUS CoreUnlink( uint32_t ulPInode,
1098                                  const char * pszName )
1099     {
1100         REDSTATUS ret;
1101 
1102         if( gpRedVolume->fReadOnly )
1103         {
1104             ret = -RED_EROFS;
1105         }
1106         else
1107         {
1108             CINODE pino;
1109 
1110             pino.ulInode = ulPInode;
1111             ret = RedInodeMount( &pino, FTYPE_DIR, false );
1112 
1113             if( ret == 0 )
1114             {
1115                 uint32_t ulDeleteIdx;
1116                 uint32_t ulInode;
1117 
1118                 ret = RedDirEntryLookup( &pino, pszName, &ulDeleteIdx, &ulInode );
1119 
1120                 if( ret == 0 )
1121                 {
1122                     ret = RedInodeBranch( &pino );
1123                 }
1124 
1125                 if( ret == 0 )
1126                 {
1127                     CINODE ino;
1128 
1129                     ino.ulInode = ulInode;
1130                     ret = RedInodeMount( &ino, FTYPE_EITHER, false );
1131 
1132                     if( ret == 0 )
1133                     {
1134                         if( ino.fDirectory && ( ino.pInodeBuf->ullSize > 0U ) )
1135                         {
1136                             ret = -RED_ENOTEMPTY;
1137                         }
1138                         else
1139                         {
1140                             #if RESERVED_BLOCKS > 0U
1141                                 gpRedCoreVol->fUseReservedBlocks = true;
1142                             #endif
1143 
1144                             ret = RedDirEntryDelete( &pino, ulDeleteIdx );
1145 
1146                             #if RESERVED_BLOCKS > 0U
1147                                 gpRedCoreVol->fUseReservedBlocks = false;
1148                             #endif
1149 
1150                             if( ret == 0 )
1151                             {
1152                                 /*  If the inode is deleted, buffers are needed to
1153                                  *  read all of the indirects and free the data
1154                                  *  blocks.  Before doing that, to reduce the
1155                                  *  minimum number of buffers needed to complete the
1156                                  *  unlink, release the parent directory inode
1157                                  *  buffers which are no longer needed.
1158                                  */
1159                                 RedInodePutCoord( &pino );
1160 
1161                                 ret = RedInodeLinkDec( &ino );
1162                                 CRITICAL_ASSERT( ret == 0 );
1163                             }
1164                         }
1165 
1166                         RedInodePut( &ino, ( ret == 0 ) ? IPUT_UPDATE_CTIME : 0U );
1167                     }
1168                 }
1169 
1170                 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1171             }
1172         }
1173 
1174         return ret;
1175     }
1176 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1)) */
1177 
1178 
1179 #if REDCONF_API_POSIX == 1
1180 
1181 /** @brief Look up the inode number of a file or directory.
1182  *
1183  *  @param ulPInode The inode number of the parent directory.
1184  *  @param pszName  The null-terminated name of the file or directory to look
1185  *                  up.
1186  *  @param pulInode On successful return, populated with the inode number named
1187  *                  by @p pszName.
1188  *
1189  *  @return A negated ::REDSTATUS code indicating the operation result.
1190  *
1191  *  @retval 0               Operation was successful.
1192  *  @retval -RED_EBADF      @p ulPInode is not a valid inode.
1193  *  @retval -RED_EINVAL     The volume is not mounted; @p pszName is `NULL`; or
1194  *                          @p pulInode is `NULL`.
1195  *  @retval -RED_EIO        A disk I/O error occurred.
1196  *  @retval -RED_ENOENT     @p pszName does not name an existing file or directory.
1197  *  @retval -RED_ENOTDIR    @p ulPInode is not a directory.
1198  */
RedCoreLookup(uint32_t ulPInode,const char * pszName,uint32_t * pulInode)1199     REDSTATUS RedCoreLookup( uint32_t ulPInode,
1200                              const char * pszName,
1201                              uint32_t * pulInode )
1202     {
1203         REDSTATUS ret;
1204 
1205         if( ( pulInode == NULL ) || !gpRedVolume->fMounted )
1206         {
1207             ret = -RED_EINVAL;
1208         }
1209         else
1210         {
1211             CINODE ino;
1212 
1213             ino.ulInode = ulPInode;
1214             ret = RedInodeMount( &ino, FTYPE_DIR, false );
1215 
1216             if( ret == 0 )
1217             {
1218                 ret = RedDirEntryLookup( &ino, pszName, NULL, pulInode );
1219 
1220                 RedInodePut( &ino, 0U );
1221             }
1222         }
1223 
1224         return ret;
1225     }
1226 #endif /* REDCONF_API_POSIX == 1 */
1227 
1228 
1229 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_RENAME == 1 )
1230 
1231 /** @brief Rename a file or directory.
1232  *
1233  *  If @p pszDstName names an existing file or directory, the behavior depends
1234  *  on the configuration.  If #REDCONF_RENAME_ATOMIC is false, and if the
1235  *  destination name exists, this function always fails with -RED_EEXIST.
1236  *
1237  *  If #REDCONF_RENAME_ATOMIC is true, and if the new name exists, then in one
1238  *  atomic operation, the destination name is unlinked and the source name is
1239  *  renamed to the destination name.  Both names must be of the same type (both
1240  *  files or both directories).  As with RedCoreUnlink(), if the destination
1241  *  name is a directory, it must be empty.  The major exception to this
1242  *  behavior is that if both names are links to the same inode, then the rename
1243  *  does nothing and both names continue to exist.
1244  *
1245  *  If the rename deletes the old destination, it may free data in the
1246  *  committed state, which will not return to free space until after a
1247  *  transaction point.  Similarly, if the deleted inode was part of the
1248  *  committed state, the inode slot will not be available until after a
1249  *  transaction point.
1250  *
1251  *  @param ulSrcPInode  The inode number of the parent directory of the file or
1252  *                      directory to rename.
1253  *  @param pszSrcName   The name of the file or directory to rename.
1254  *  @param ulDstPInode  The new parent directory inode number of the file or
1255  *                      directory after the rename.
1256  *  @param pszDstName   The new name of the file or directory after the rename.
1257  *
1258  *  @return A negated ::REDSTATUS code indicating the operation result.
1259  *
1260  *  @retval 0                   Operation was successful.
1261  *  @retval -RED_EBADF          @p ulSrcPInode is not a valid inode number; or
1262  *                              @p ulDstPInode is not a valid inode number.
1263  *  @retval -RED_EEXIST         #REDCONF_RENAME_POSIX is false and the
1264  *                              destination name exists.
1265  *  @retval -RED_EINVAL         The volume is not mounted; @p pszSrcName is
1266  *                              `NULL`; or @p pszDstName is `NULL`.
1267  *  @retval -RED_EIO            A disk I/O error occurred.
1268  *  @retval -RED_EISDIR         The destination name exists and is a directory,
1269  *                              and the source name is a non-directory.
1270  *  @retval -RED_ENAMETOOLONG   Either @p pszSrcName or @p pszDstName is longer
1271  *                              than #REDCONF_NAME_MAX.
1272  *  @retval -RED_ENOENT         The source name is not an existing entry; or
1273  *                              either @p pszSrcName or @p pszDstName point to
1274  *                              an empty string.
1275  *  @retval -RED_ENOTDIR        @p ulSrcPInode is not a directory; or
1276  *                              @p ulDstPInode is not a directory; or the source
1277  *                              name is a directory and the destination name is
1278  *                              a file.
1279  *  @retval -RED_ENOTEMPTY      The destination name is a directory which is not
1280  *                              empty.
1281  *  @retval -RED_ENOSPC         The file system does not have enough space to
1282  *                              extend the @p ulDstPInode directory.
1283  *  @retval -RED_EROFS          The directory to be removed resides on a
1284  *                              read-only file system.
1285  */
RedCoreRename(uint32_t ulSrcPInode,const char * pszSrcName,uint32_t ulDstPInode,const char * pszDstName)1286     REDSTATUS RedCoreRename( uint32_t ulSrcPInode,
1287                              const char * pszSrcName,
1288                              uint32_t ulDstPInode,
1289                              const char * pszDstName )
1290     {
1291         REDSTATUS ret;
1292 
1293         if( !gpRedVolume->fMounted )
1294         {
1295             ret = -RED_EINVAL;
1296         }
1297         else if( gpRedVolume->fReadOnly )
1298         {
1299             ret = -RED_EROFS;
1300         }
1301         else
1302         {
1303             ret = CoreRename( ulSrcPInode, pszSrcName, ulDstPInode, pszDstName );
1304 
1305             if( ( ret == -RED_ENOSPC ) &&
1306                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1307                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1308             {
1309                 ret = RedVolTransact();
1310 
1311                 if( ret == 0 )
1312                 {
1313                     ret = CoreRename( ulSrcPInode, pszSrcName, ulDstPInode, pszDstName );
1314                 }
1315             }
1316 
1317             if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_RENAME ) != 0U ) )
1318             {
1319                 ret = RedVolTransact();
1320             }
1321         }
1322 
1323         return ret;
1324     }
1325 
1326 
1327 /** @brief Rename a file or directory.
1328  *
1329  *  @param ulSrcPInode  The inode number of the parent directory of the file or
1330  *                      directory to rename.
1331  *  @param pszSrcName   The name of the file or directory to rename.
1332  *  @param ulDstPInode  The new parent directory inode number of the file or
1333  *                      directory after the rename.
1334  *  @param pszDstName   The new name of the file or directory after the rename.
1335  *
1336  *  @return A negated ::REDSTATUS code indicating the operation result.
1337  *
1338  *  @retval 0                   Operation was successful.
1339  *  @retval -RED_EBADF          @p ulSrcPInode is not a valid inode number; or
1340  *                              @p ulDstPInode is not a valid inode number.
1341  *  @retval -RED_EEXIST         #REDCONF_RENAME_POSIX is false and the
1342  *                              destination name exists.
1343  *  @retval -RED_EIO            A disk I/O error occurred.
1344  *  @retval -RED_EISDIR         The destination name exists and is a directory,
1345  *                              and the source name is a non-directory.
1346  *  @retval -RED_ENAMETOOLONG   Either @p pszSrcName or @p pszDstName is longer
1347  *                              than #REDCONF_NAME_MAX.
1348  *  @retval -RED_ENOENT         The source name is not an existing entry; or
1349  *                              either @p pszSrcName or @p pszDstName point to
1350  *                              an empty string.
1351  *  @retval -RED_ENOTDIR        @p ulSrcPInode is not a directory; or
1352  *                              @p ulDstPInode is not a directory; or the source
1353  *                              name is a directory and the destination name is
1354  *                              a file.
1355  *  @retval -RED_ENOTEMPTY      The destination name is a directory which is not
1356  *                              empty.
1357  *  @retval -RED_ENOSPC         The file system does not have enough space to
1358  *                              extend the @p ulDstPInode directory.
1359  *  @retval -RED_EROFS          The directory to be removed resides on a
1360  *                              read-only file system.
1361  */
CoreRename(uint32_t ulSrcPInode,const char * pszSrcName,uint32_t ulDstPInode,const char * pszDstName)1362     static REDSTATUS CoreRename( uint32_t ulSrcPInode,
1363                                  const char * pszSrcName,
1364                                  uint32_t ulDstPInode,
1365                                  const char * pszDstName )
1366     {
1367         REDSTATUS ret;
1368 
1369         if( gpRedVolume->fReadOnly )
1370         {
1371             ret = -RED_EROFS;
1372         }
1373         else
1374         {
1375             bool fUpdateTimestamps = false;
1376             CINODE SrcPInode;
1377 
1378             SrcPInode.ulInode = ulSrcPInode;
1379             ret = RedInodeMount( &SrcPInode, FTYPE_DIR, true );
1380 
1381             if( ret == 0 )
1382             {
1383                 CINODE DstPInode;
1384                 CINODE * pDstPInode;
1385 
1386                 if( ulSrcPInode == ulDstPInode )
1387                 {
1388                     pDstPInode = &SrcPInode;
1389                 }
1390                 else
1391                 {
1392                     pDstPInode = &DstPInode;
1393                     DstPInode.ulInode = ulDstPInode;
1394                     ret = RedInodeMount( pDstPInode, FTYPE_DIR, true );
1395                 }
1396 
1397                 if( ret == 0 )
1398                 {
1399                     /*  Initialize these to zero so we can unconditionally put them,
1400                      *  even if RedDirEntryRename() fails before mounting them.
1401                      */
1402                     CINODE SrcInode = { 0U };
1403                     CINODE DstInode = { 0U };
1404 
1405                     ret = RedDirEntryRename( &SrcPInode, pszSrcName, &SrcInode, pDstPInode, pszDstName, &DstInode );
1406 
1407                     #if REDCONF_RENAME_ATOMIC == 1
1408                         if( ( ret == 0 ) && ( DstInode.ulInode != INODE_INVALID ) && ( DstInode.ulInode != SrcInode.ulInode ) )
1409                         {
1410                             /*  If the inode is deleted, buffers are needed to read all
1411                              *  of the indirects and free the data blocks.  Before doing
1412                              *  that, to reduce the minimum number of buffers needed to
1413                              *  complete the rename, release parent directory inode
1414                              *  buffers which are no longer needed.
1415                              */
1416                             RedInodePutCoord( &SrcPInode );
1417                             RedInodePutCoord( pDstPInode );
1418 
1419                             ret = RedInodeLinkDec( &DstInode );
1420                             CRITICAL_ASSERT( ret == 0 );
1421                         }
1422 
1423                         if( ( ret == 0 ) && ( DstInode.ulInode != SrcInode.ulInode ) )
1424                     #else /* if REDCONF_RENAME_ATOMIC == 1 */
1425                         if( ret == 0 )
1426                     #endif /* if REDCONF_RENAME_ATOMIC == 1 */
1427                     {
1428                         fUpdateTimestamps = true;
1429                     }
1430 
1431                     #if REDCONF_RENAME_ATOMIC == 1
1432                         RedInodePut( &DstInode, 0U );
1433                     #endif
1434 
1435                     /*  POSIX says updating ctime for the source inode is optional,
1436                      *  but searching around it looks like this is common for Linux
1437                      *  and other Unix file systems.
1438                      */
1439                     RedInodePut( &SrcInode, fUpdateTimestamps ? IPUT_UPDATE_CTIME : 0U );
1440                     RedInodePut( pDstPInode, fUpdateTimestamps ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1441                 }
1442             }
1443 
1444             RedInodePut( &SrcPInode, fUpdateTimestamps ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1445         }
1446 
1447         return ret;
1448     }
1449 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_RENAME == 1) */
1450 
1451 
1452 #if REDCONF_API_POSIX == 1
1453 
1454 /** @brief Get the status of a file or directory.
1455  *
1456  *  See the ::REDSTAT type for the details of the information returned.
1457  *
1458  *  @param ulInode  The inode number of the file or directory whose information
1459  *                  is to be retrieved.
1460  *  @param pStat    Pointer to a ::REDSTAT buffer to populate.
1461  *
1462  *  @return A negated ::REDSTATUS code indicating the operation result.
1463  *
1464  *  @retval 0           Operation was successful.
1465  *  @retval -RED_EBADF  @p ulInode is not a valid inode.
1466  *  @retval -RED_EINVAL The volume is not mounted; @p pStat is `NULL`.
1467  *  @retval -RED_EIO    A disk I/O error occurred.
1468  */
RedCoreStat(uint32_t ulInode,REDSTAT * pStat)1469     REDSTATUS RedCoreStat( uint32_t ulInode,
1470                            REDSTAT * pStat )
1471     {
1472         REDSTATUS ret;
1473 
1474         if( !gpRedVolume->fMounted || ( pStat == NULL ) )
1475         {
1476             ret = -RED_EINVAL;
1477         }
1478         else
1479         {
1480             CINODE ino;
1481 
1482             ino.ulInode = ulInode;
1483             ret = RedInodeMount( &ino, FTYPE_EITHER, false );
1484 
1485             if( ret == 0 )
1486             {
1487                 RedMemSet( pStat, 0U, sizeof( *pStat ) );
1488 
1489                 pStat->st_dev = gbRedVolNum;
1490                 pStat->st_ino = ulInode;
1491                 pStat->st_mode = ino.pInodeBuf->uMode;
1492                 #if REDCONF_API_POSIX_LINK == 1
1493                     pStat->st_nlink = ino.pInodeBuf->uNLink;
1494                 #else
1495                     pStat->st_nlink = 1U;
1496                 #endif
1497                 pStat->st_size = ino.pInodeBuf->ullSize;
1498                 #if REDCONF_INODE_TIMESTAMPS == 1
1499                     pStat->st_atime = ino.pInodeBuf->ulATime;
1500                     pStat->st_mtime = ino.pInodeBuf->ulMTime;
1501                     pStat->st_ctime = ino.pInodeBuf->ulCTime;
1502                 #endif
1503                 #if REDCONF_INODE_BLOCKS == 1
1504                     pStat->st_blocks = ino.pInodeBuf->ulBlocks;
1505                 #endif
1506 
1507                 RedInodePut( &ino, 0U );
1508             }
1509         }
1510 
1511         return ret;
1512     }
1513 #endif /* REDCONF_API_POSIX == 1 */
1514 
1515 
1516 #if REDCONF_API_FSE == 1
1517 
1518 /** @brief Get the size of a file.
1519  *
1520  *  @param ulInode  The inode number of the file whose size is to be retrieved.
1521  *  @param pullSize On successful exit, populated with the file size.
1522  *
1523  *  @return A negated ::REDSTATUS code indicating the operation result.
1524  *
1525  *  @retval 0           Operation was successful.
1526  *  @retval -RED_EBADF  @p ulInode is not a valid inode.
1527  *  @retval -RED_EINVAL The volume is not mounted; @p pullSize is `NULL`.
1528  *  @retval -RED_EIO    A disk I/O error occurred.
1529  *  @retval -RED_EISDIR @p ulInode is a directory inode.
1530  */
RedCoreFileSizeGet(uint32_t ulInode,uint64_t * pullSize)1531     REDSTATUS RedCoreFileSizeGet( uint32_t ulInode,
1532                                   uint64_t * pullSize )
1533     {
1534         REDSTATUS ret;
1535 
1536         if( !gpRedVolume->fMounted || ( pullSize == NULL ) )
1537         {
1538             ret = -RED_EINVAL;
1539         }
1540         else
1541         {
1542             CINODE ino;
1543 
1544             ino.ulInode = ulInode;
1545             ret = RedInodeMount( &ino, FTYPE_FILE, false );
1546 
1547             if( ret == 0 )
1548             {
1549                 *pullSize = ino.pInodeBuf->ullSize;
1550 
1551                 RedInodePut( &ino, 0U );
1552             }
1553         }
1554 
1555         return ret;
1556     }
1557 #endif /* REDCONF_API_FSE == 1 */
1558 
1559 
1560 /** @brief Read from a file.
1561  *
1562  *  Data which has not yet been written, but which is before the end-of-file
1563  *  (sparse data), shall read as zeroes.  A short read -- where the number of
1564  *  bytes read is less than requested -- indicates that the requested read was
1565  *  partially or, if zero bytes were read, entirely beyond the end-of-file.
1566  *
1567  *  If @p ullStart is at or beyond the maximum file size, it is treated like
1568  *  any other read entirely beyond the end-of-file: no data is read and
1569  *  @p pulLen is populated with zero.
1570  *
1571  *  @param ulInode  The inode number of the file to read.
1572  *  @param ullStart The file offset to read from.
1573  *  @param pulLen   On entry, contains the number of bytes to read; on
1574  *                  successful exit, contains the number of bytes actually
1575  *                  read.
1576  *  @param pBuffer  The buffer to populate with the data read.  Must be big
1577  *                  enough for the read request.
1578  *
1579  *  @return A negated ::REDSTATUS code indicating the operation result.
1580  *
1581  *  @retval 0           Operation was successful.
1582  *  @retval -RED_EBADF  @p ulInode is not a valid inode number.
1583  *  @retval -RED_EINVAL The volume is not mounted; or @p pBuffer is `NULL`.
1584  *  @retval -RED_EIO    A disk I/O error occurred.
1585  *  @retval -RED_EISDIR The inode is a directory inode.
1586  */
RedCoreFileRead(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,void * pBuffer)1587 REDSTATUS RedCoreFileRead( uint32_t ulInode,
1588                            uint64_t ullStart,
1589                            uint32_t * pulLen,
1590                            void * pBuffer )
1591 {
1592     REDSTATUS ret;
1593 
1594     if( !gpRedVolume->fMounted || ( pulLen == NULL ) )
1595     {
1596         ret = -RED_EINVAL;
1597     }
1598     else
1599     {
1600         #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1601             bool fUpdateAtime = ( *pulLen > 0U ) && !gpRedVolume->fReadOnly;
1602         #else
1603             bool fUpdateAtime = false;
1604         #endif
1605         CINODE ino;
1606 
1607         ino.ulInode = ulInode;
1608         ret = RedInodeMount( &ino, FTYPE_FILE, fUpdateAtime );
1609 
1610         if( ret == 0 )
1611         {
1612             ret = RedInodeDataRead( &ino, ullStart, pulLen, pBuffer );
1613 
1614             #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1615                 RedInodePut( &ino, ( ( ret == 0 ) && fUpdateAtime ) ? IPUT_UPDATE_ATIME : 0U );
1616             #else
1617                 RedInodePut( &ino, 0U );
1618             #endif
1619         }
1620     }
1621 
1622     return ret;
1623 }
1624 
1625 
1626 #if REDCONF_READ_ONLY == 0
1627 
1628 /** @brief Write to a file.
1629  *
1630  *  If the write extends beyond the end-of-file, the file size will be
1631  *  increased.
1632  *
1633  *  A short write -- where the number of bytes written is less than requested
1634  *  -- indicates either that the file system ran out of space but was still
1635  *  able to write some of the request; or that the request would have caused
1636  *  the file to exceed the maximum file size, but some of the data could be
1637  *  written prior to the file size limit.
1638  *
1639  *  If an error is returned, either none of the data was written or a critical
1640  *  error occurred (like an I/O error) and the file system volume will be
1641  *  read-only.
1642  *
1643  *  @param ulInode  The file number of the file to write.
1644  *  @param ullStart The file offset to write at.
1645  *  @param pulLen   On entry, the number of bytes to write; on successful exit,
1646  *                  the number of bytes actually written.
1647  *  @param pBuffer  The buffer containing the data to be written.  Must big
1648  *                  enough for the write request.
1649  *
1650  *  @return A negated ::REDSTATUS code indicating the operation result.
1651  *
1652  *  @retval 0           Operation was successful.
1653  *  @retval -RED_EBADF  @p ulInode is not a valid file number.
1654  *  @retval -RED_EFBIG  No data can be written to the given file offset since
1655  *                      the resulting file size would exceed the maximum file
1656  *                      size.
1657  *  @retval -RED_EINVAL The volume is not mounted; or @p pBuffer is `NULL`.
1658  *  @retval -RED_EIO    A disk I/O error occurred.
1659  *  @retval -RED_EISDIR The inode is a directory inode.
1660  *  @retval -RED_ENOSPC No data can be written because there is insufficient
1661  *                      free space.
1662  *  @retval -RED_EROFS  The file system volume is read-only.
1663  */
RedCoreFileWrite(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,const void * pBuffer)1664     REDSTATUS RedCoreFileWrite( uint32_t ulInode,
1665                                 uint64_t ullStart,
1666                                 uint32_t * pulLen,
1667                                 const void * pBuffer )
1668     {
1669         REDSTATUS ret;
1670 
1671         if( !gpRedVolume->fMounted )
1672         {
1673             ret = -RED_EINVAL;
1674         }
1675         else if( gpRedVolume->fReadOnly )
1676         {
1677             ret = -RED_EROFS;
1678         }
1679         else
1680         {
1681             ret = CoreFileWrite( ulInode, ullStart, pulLen, pBuffer );
1682 
1683             if( ( ret == -RED_ENOSPC ) &&
1684                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1685                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1686             {
1687                 ret = RedVolTransact();
1688 
1689                 if( ret == 0 )
1690                 {
1691                     ret = CoreFileWrite( ulInode, ullStart, pulLen, pBuffer );
1692                 }
1693             }
1694 
1695             if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_WRITE ) != 0U ) )
1696             {
1697                 ret = RedVolTransact();
1698             }
1699         }
1700 
1701         return ret;
1702     }
1703 
1704 
1705 /** @brief Write to a file.
1706  *
1707  *  @param ulInode  The file number of the file to write.
1708  *  @param ullStart The file offset to write at.
1709  *  @param pulLen   On entry, the number of bytes to write; on successful exit,
1710  *                  the number of bytes actually written.
1711  *  @param pBuffer  The buffer containing the data to be written.  Must big
1712  *                  enough for the write request.
1713  *
1714  *  @return A negated ::REDSTATUS code indicating the operation result.
1715  *
1716  *  @retval 0           Operation was successful.
1717  *  @retval -RED_EBADF  @p ulInode is not a valid file number.
1718  *  @retval -RED_EFBIG  No data can be written to the given file offset since
1719  *                      the resulting file size would exceed the maximum file
1720  *                      size.
1721  *  @retval -RED_EIO    A disk I/O error occurred.
1722  *  @retval -RED_EISDIR The inode is a directory inode.
1723  *  @retval -RED_ENOSPC No data can be written because there is insufficient
1724  *                      free space.
1725  *  @retval -RED_EROFS  The file system volume is read-only.
1726  */
CoreFileWrite(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,const void * pBuffer)1727     static REDSTATUS CoreFileWrite( uint32_t ulInode,
1728                                     uint64_t ullStart,
1729                                     uint32_t * pulLen,
1730                                     const void * pBuffer )
1731     {
1732         REDSTATUS ret;
1733 
1734         if( gpRedVolume->fReadOnly )
1735         {
1736             ret = -RED_EROFS;
1737         }
1738         else
1739         {
1740             CINODE ino;
1741 
1742             ino.ulInode = ulInode;
1743             ret = RedInodeMount( &ino, FTYPE_FILE, true );
1744 
1745             if( ret == 0 )
1746             {
1747                 ret = RedInodeDataWrite( &ino, ullStart, pulLen, pBuffer );
1748 
1749                 RedInodePut( &ino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1750             }
1751         }
1752 
1753         return ret;
1754     }
1755 #endif /* REDCONF_READ_ONLY == 0 */
1756 
1757 
1758 #if TRUNCATE_SUPPORTED
1759 
1760 /** @brief Set the file size.
1761  *
1762  *  Allows the file size to be increased, decreased, or to remain the same.  If
1763  *  the file size is increased, the new area is sparse (will read as zeroes).
1764  *  If the file size is decreased, the data beyond the new end-of-file will
1765  *  return to free space once it is no longer part of the committed state
1766  *  (either immediately or after the next transaction point).
1767  *
1768  *  @param ulInode  The inode of the file to truncate.
1769  *  @param ullSize  The new file size, in bytes.
1770  *
1771  *  @return A negated ::REDSTATUS code indicating the operation result.
1772  *
1773  *  @retval 0           Operation was successful.
1774  *  @retval -RED_EBADF  @p ulInode is not a valid inode number.
1775  *  @retval -RED_EFBIG  @p ullSize exceeds the maximum file size.
1776  *  @retval -RED_EINVAL The volume is not mounted.
1777  *  @retval -RED_EIO    A disk I/O error occurred.
1778  *  @retval -RED_EISDIR The inode is a directory inode.
1779  *  @retval -RED_ENOSPC Insufficient free space to perform the truncate.
1780  *  @retval -RED_EROFS  The file system volume is read-only.
1781  */
RedCoreFileTruncate(uint32_t ulInode,uint64_t ullSize)1782     REDSTATUS RedCoreFileTruncate( uint32_t ulInode,
1783                                    uint64_t ullSize )
1784     {
1785         REDSTATUS ret;
1786 
1787         if( !gpRedVolume->fMounted )
1788         {
1789             ret = -RED_EINVAL;
1790         }
1791         else if( gpRedVolume->fReadOnly )
1792         {
1793             ret = -RED_EROFS;
1794         }
1795         else
1796         {
1797             ret = CoreFileTruncate( ulInode, ullSize );
1798 
1799             if( ( ret == -RED_ENOSPC ) &&
1800                 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1801                 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1802             {
1803                 ret = RedVolTransact();
1804 
1805                 if( ret == 0 )
1806                 {
1807                     ret = CoreFileTruncate( ulInode, ullSize );
1808                 }
1809             }
1810 
1811             if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_TRUNCATE ) != 0U ) )
1812             {
1813                 ret = RedVolTransact();
1814             }
1815         }
1816 
1817         return ret;
1818     }
1819 
1820 
1821 /** @brief Set the file size.
1822  *
1823  *  @param ulInode  The inode of the file to truncate.
1824  *  @param ullSize  The new file size, in bytes.
1825  *
1826  *  @return A negated ::REDSTATUS code indicating the operation result.
1827  *
1828  *  @retval 0           Operation was successful.
1829  *  @retval -RED_EBADF  @p ulInode is not a valid inode number.
1830  *  @retval -RED_EFBIG  @p ullSize exceeds the maximum file size.
1831  *  @retval -RED_EIO    A disk I/O error occurred.
1832  *  @retval -RED_EISDIR The inode is a directory inode.
1833  *  @retval -RED_ENOSPC Insufficient free space to perform the truncate.
1834  *  @retval -RED_EROFS  The file system volume is read-only.
1835  */
CoreFileTruncate(uint32_t ulInode,uint64_t ullSize)1836     static REDSTATUS CoreFileTruncate( uint32_t ulInode,
1837                                        uint64_t ullSize )
1838     {
1839         REDSTATUS ret;
1840 
1841         if( gpRedVolume->fReadOnly )
1842         {
1843             ret = -RED_EROFS;
1844         }
1845         else
1846         {
1847             CINODE ino;
1848 
1849             ino.ulInode = ulInode;
1850             ret = RedInodeMount( &ino, FTYPE_FILE, true );
1851 
1852             if( ret == 0 )
1853             {
1854                 #if RESERVED_BLOCKS > 0U
1855                     gpRedCoreVol->fUseReservedBlocks = ( ullSize < ino.pInodeBuf->ullSize );
1856                 #endif
1857 
1858                 ret = RedInodeDataTruncate( &ino, ullSize );
1859 
1860                 #if RESERVED_BLOCKS > 0U
1861                     gpRedCoreVol->fUseReservedBlocks = false;
1862                 #endif
1863 
1864                 RedInodePut( &ino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1865             }
1866         }
1867 
1868         return ret;
1869     }
1870 #endif /* TRUNCATE_SUPPORTED */
1871 
1872 
1873 #if ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_READDIR == 1 )
1874 
1875 /** @brief Read from a directory.
1876  *
1877  *  If files are added to the directory after it is opened, the new files may
1878  *  or may not be returned by this function.  If files are deleted, the deleted
1879  *  files will not be returned.
1880  *
1881  *  @param ulInode  The directory inode to read from.
1882  *  @param pulPos   A token which stores the position within the directory.  To
1883  *                  read from the beginning of the directory, populate with
1884  *                  zero.
1885  *  @param pszName  Pointer to a buffer which must be big enough to store a
1886  *                  maximum size name, including a null terminator.  On
1887  *                  successful exit, populated with the name of the next
1888  *                  directory entry.
1889  *  @param pulInode On successful return, populated with the inode number of the
1890  *                  next directory entry.
1891  *
1892  *  @return A negated ::REDSTATUS code indicating the operation result.
1893  *
1894  *  @retval 0               Operation was successful.
1895  *  @retval -RED_EBADF      @p ulInode is not a valid inode number.
1896  *  @retval -RED_EINVAL     The volume is not mounted.
1897  *  @retval -RED_EIO        A disk I/O error occurred.
1898  *  @retval -RED_ENOENT     There are no more entries in the directory.
1899  *  @retval -RED_ENOTDIR    @p ulInode refers to a file.
1900  */
RedCoreDirRead(uint32_t ulInode,uint32_t * pulPos,char * pszName,uint32_t * pulInode)1901     REDSTATUS RedCoreDirRead( uint32_t ulInode,
1902                               uint32_t * pulPos,
1903                               char * pszName,
1904                               uint32_t * pulInode )
1905     {
1906         REDSTATUS ret;
1907 
1908         if( !gpRedVolume->fMounted )
1909         {
1910             ret = -RED_EINVAL;
1911         }
1912         else
1913         {
1914             CINODE ino;
1915 
1916             ino.ulInode = ulInode;
1917             ret = RedInodeMount( &ino, FTYPE_DIR, false );
1918 
1919             if( ret == 0 )
1920             {
1921                 ret = RedDirEntryRead( &ino, pulPos, pszName, pulInode );
1922 
1923                 #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1924                     if( ( ret == 0 ) && !gpRedVolume->fReadOnly )
1925                     {
1926                         ret = RedInodeBranch( &ino );
1927                     }
1928 
1929                     RedInodePut( &ino, ( ( ret == 0 ) && !gpRedVolume->fReadOnly ) ? IPUT_UPDATE_ATIME : 0U );
1930                 #else
1931                     RedInodePut( &ino, 0U );
1932                 #endif
1933             }
1934         }
1935 
1936         return ret;
1937     }
1938 #endif /* (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_READDIR == 1) */
1939