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 Implementation of the the Reliance Edge POSIX-like API.
29  */
30 
31 #include <redfs.h>
32 
33 #if REDCONF_API_POSIX == 1
34 
35 /** @defgroup red_group_posix The POSIX-like File System Interface
36  *  @{
37  */
38 
39     #include <redvolume.h>
40     #include <redcoreapi.h>
41     #include <redposix.h>
42     #include <redpath.h>
43 
44 
45 /*-------------------------------------------------------------------
46  *   File Descriptors
47  *  -------------------------------------------------------------------*/
48 
49     #define FD_GEN_BITS    11U /* File descriptor bits for mount generation. */
50     #define FD_VOL_BITS    8U  /* File descriptor bits for volume number. */
51     #define FD_IDX_BITS    12U /* File descriptor bits for handle index. */
52 
53 /*  31 bits available: file descriptors are int32_t, but the sign bit must
54  *  always be zero.
55  */
56     #if ( FD_GEN_BITS + FD_VOL_BITS + FD_IDX_BITS ) > 31U
57         #error "Internal error: too many file descriptor bits!"
58     #endif
59 
60 /*  Maximum values for file descriptor components.
61  */
62     #define FD_GEN_MAX    ( ( 1UL << FD_GEN_BITS ) - 1U )
63     #define FD_VOL_MAX    ( ( 1UL << FD_VOL_BITS ) - 1U )
64     #define FD_IDX_MAX    ( ( 1UL << FD_IDX_BITS ) - 1U )
65 
66     #if REDCONF_VOLUME_COUNT > FD_VOL_MAX
67         #error "Error: Too many file system volumes!"
68     #endif
69     #if REDCONF_HANDLE_COUNT > ( FD_IDX_MAX + 1U )
70         #error "Error: Too many file system handles!"
71     #endif
72 
73 /*  File descriptors must never be negative; and must never be zero, one, or
74  *  two, to avoid confusion with STDIN, STDOUT, and STDERR.
75  */
76     #define FD_MIN    ( 3 )
77 
78 /*-------------------------------------------------------------------
79  *   Handles
80  *  -------------------------------------------------------------------*/
81 
82 /*  Mask of all RED_O_* values.
83  */
84     #define RED_O_MASK         ( RED_O_RDONLY | RED_O_WRONLY | RED_O_RDWR | RED_O_APPEND | RED_O_CREAT | RED_O_EXCL | RED_O_TRUNC )
85 
86     #define HFLAG_DIRECTORY    0x01U /* Handle is for a directory. */
87     #define HFLAG_READABLE     0x02U /* Handle is readable. */
88     #define HFLAG_WRITEABLE    0x04U /* Handle is writeable. */
89     #define HFLAG_APPENDING    0x08U /* Handle was opened in append mode. */
90 
91 /*  @brief Handle structure, used to implement file descriptors and directory
92  *         streams.
93  */
94     typedef struct sREDHANDLE
95     {
96         uint32_t ulInode;     /**< Inode number; 0 if handle is available. */
97         uint8_t bVolNum;      /**< Volume containing the inode. */
98         uint8_t bFlags;       /**< Handle flags (type and mode). */
99         uint64_t ullOffset;   /**< File or directory offset. */
100         #if REDCONF_API_POSIX_READDIR == 1
101             REDDIRENT dirent; /**< Dirent structure returned by red_readdir(). */
102         #endif
103     } REDHANDLE;
104 
105 /*-------------------------------------------------------------------
106  *   Tasks
107  *  -------------------------------------------------------------------*/
108 
109     #if REDCONF_TASK_COUNT > 1U
110 
111 /*  @brief Per-task information.
112  */
113         typedef struct
114         {
115             uint32_t ulTaskId; /**< ID of the task which owns this slot; 0 if free. */
116             REDSTATUS iErrno;  /**< Last error value. */
117         } TASKSLOT;
118     #endif
119 
120 /*-------------------------------------------------------------------
121  *   Local Prototypes
122  *  -------------------------------------------------------------------*/
123 
124     #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
125         static REDSTATUS UnlinkSub( const char * pszPath,
126                                     FTYPE type );
127     #endif
128     static REDSTATUS FildesOpen( const char * pszPath,
129                                  uint32_t ulOpenMode,
130                                  FTYPE type,
131                                  int32_t * piFildes );
132     static REDSTATUS FildesClose( int32_t iFildes );
133     static REDSTATUS FildesToHandle( int32_t iFildes,
134                                      FTYPE expectedType,
135                                      REDHANDLE ** ppHandle );
136     static int32_t FildesPack( uint16_t uHandleIdx,
137                                uint8_t bVolNum );
138     static void FildesUnpack( int32_t iFildes,
139                               uint16_t * puHandleIdx,
140                               uint8_t * pbVolNum,
141                               uint16_t * puGeneration );
142     #if REDCONF_API_POSIX_READDIR == 1
143         static bool DirStreamIsValid( const REDDIR * pDirStream );
144     #endif
145     static REDSTATUS PosixEnter( void );
146     static void PosixLeave( void );
147     static REDSTATUS ModeTypeCheck( uint16_t uMode,
148                                     FTYPE expectedType );
149     #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) )
150         static REDSTATUS InodeUnlinkCheck( uint32_t ulInode );
151     #endif
152     #if REDCONF_TASK_COUNT > 1U
153         static REDSTATUS TaskRegister( uint32_t * pulTaskIdx );
154     #endif
155     static int32_t PosixReturn( REDSTATUS iError );
156 
157 /*-------------------------------------------------------------------
158  *   Globals
159  *  -------------------------------------------------------------------*/
160 
161     static bool gfPosixInited;                         /* Whether driver is initialized. */
162     static REDHANDLE gaHandle[ REDCONF_HANDLE_COUNT ]; /* Array of all handles. */
163     #if REDCONF_TASK_COUNT > 1U
164         static TASKSLOT gaTask[ REDCONF_TASK_COUNT ];  /* Array of task slots. */
165     #endif
166 
167 /*  Array of volume mount "generations".  These are incremented for a volume
168  *  each time that volume is mounted.  The generation number (along with the
169  *  volume number) is incorporated into the file descriptors; a stale file
170  *  descriptor from a previous mount can be detected since it will include a
171  *  stale generation number.
172  */
173     static uint16_t gauGeneration[ REDCONF_VOLUME_COUNT ];
174 
175 
176 /*-------------------------------------------------------------------
177  *   Public API
178  *  -------------------------------------------------------------------*/
179 
180 /** @brief Initialize the Reliance Edge file system driver.
181  *
182  *  Prepares the Reliance Edge file system driver to be used.  Must be the first
183  *  Reliance Edge function to be invoked: no volumes can be mounted or formatted
184  *  until the driver has been initialized.
185  *
186  *  If this function is called when the Reliance Edge driver is already
187  *  initialized, it does nothing and returns success.
188  *
189  *  This function is not thread safe: attempting to initialize from multiple
190  *  threads could leave things in a bad state.
191  *
192  *  @return On success, zero is returned.  On error, -1 is returned and
193  #red_errno is set appropriately.
194  *
195  *  <b>Errno values</b>
196  *  - #RED_EINVAL: The volume path prefix configuration is invalid.
197  */
red_init(void)198     int32_t red_init( void )
199     {
200         REDSTATUS ret;
201 
202         if( gfPosixInited )
203         {
204             ret = 0;
205         }
206         else
207         {
208             ret = RedCoreInit();
209 
210             if( ret == 0 )
211             {
212                 RedMemSet( gaHandle, 0U, sizeof( gaHandle ) );
213 
214                 #if REDCONF_TASK_COUNT > 1U
215                     RedMemSet( gaTask, 0U, sizeof( gaTask ) );
216                 #endif
217 
218                 gfPosixInited = true;
219             }
220         }
221 
222         return PosixReturn( ret );
223     }
224 
225 
226 /** @brief Uninitialize the Reliance Edge file system driver.
227  *
228  *  Tears down the Reliance Edge file system driver.  Cannot be used until all
229  *  Reliance Edge volumes are unmounted.  A subsequent call to red_init() will
230  *  initialize the driver again.
231  *
232  *  If this function is called when the Reliance Edge driver is already
233  *  uninitialized, it does nothing and returns success.
234  *
235  *  This function is not thread safe: attempting to uninitialize from multiple
236  *  threads could leave things in a bad state.
237  *
238  *  @return On success, zero is returned.  On error, -1 is returned and
239  #red_errno is set appropriately.
240  *
241  *  <b>Errno values</b>
242  *  - #RED_EBUSY: At least one volume is still mounted.
243  */
red_uninit(void)244     int32_t red_uninit( void )
245     {
246         REDSTATUS ret;
247 
248         if( gfPosixInited )
249         {
250             ret = PosixEnter();
251 
252             if( ret == 0 )
253             {
254                 uint8_t bVolNum;
255 
256                 for( bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++ )
257                 {
258                     if( gaRedVolume[ bVolNum ].fMounted )
259                     {
260                         ret = -RED_EBUSY;
261                         break;
262                     }
263                 }
264 
265                 if( ret == 0 )
266                 {
267                     /*  All volumes are unmounted.  Mark the driver as
268                      *  uninitialized before releasing the FS mutex, to avoid any
269                      *  race condition where a volume could be mounted and then the
270                      *  driver uninitialized with a mounted volume.
271                      */
272                     gfPosixInited = false;
273                 }
274 
275                 /*  The FS mutex must be released before we uninitialize the core,
276                  *  since the FS mutex needs to be in the released state when it
277                  *  gets uninitialized.
278                  *
279                  *  Don't use PosixLeave(), since it asserts gfPosixInited is true.
280                  */
281                 #if REDCONF_TASK_COUNT > 1U
282                     RedOsMutexRelease();
283                 #endif
284             }
285 
286             if( ret == 0 )
287             {
288                 ret = RedCoreUninit();
289 
290                 /*  Not good if the above fails, since things might be partly, but
291                  *  not entirely, torn down, and there might not be a way back to
292                  *  a valid driver state.
293                  */
294                 REDASSERT( ret == 0 );
295             }
296         }
297         else
298         {
299             ret = 0;
300         }
301 
302         return PosixReturn( ret );
303     }
304 
305 
306 /** @brief Mount a file system volume.
307  *
308  *  Prepares the file system volume to be accessed.  Mount will fail if the
309  *  volume has never been formatted, or if the on-disk format is inconsistent
310  *  with the compile-time configuration.
311  *
312  *  An error is returned if the volume is already mounted.
313  *
314  *  @param pszVolume    A path prefix identifying the volume to mount.
315  *
316  *  @return On success, zero is returned.  On error, -1 is returned and
317  #red_errno is set appropriately.
318  *
319  *  <b>Errno values</b>
320  *  - #RED_EBUSY: Volume is already mounted.
321  *  - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized.
322  *  - #RED_EIO: Volume not formatted, improperly formatted, or corrupt.
323  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
324  *  - #RED_EUSERS: Cannot become a file system user: too many users.
325  */
red_mount(const char * pszVolume)326     int32_t red_mount( const char * pszVolume )
327     {
328         REDSTATUS ret;
329 
330         ret = PosixEnter();
331 
332         if( ret == 0 )
333         {
334             uint8_t bVolNum;
335 
336             ret = RedPathSplit( pszVolume, &bVolNum, NULL );
337 
338             /*  The core will return success if the volume is already mounted, so
339              *  check for that condition here to propagate the error.
340              */
341             if( ( ret == 0 ) && gaRedVolume[ bVolNum ].fMounted )
342             {
343                 ret = -RED_EBUSY;
344             }
345 
346             #if REDCONF_VOLUME_COUNT > 1U
347                 if( ret == 0 )
348                 {
349                     ret = RedCoreVolSetCurrent( bVolNum );
350                 }
351             #endif
352 
353             if( ret == 0 )
354             {
355                 ret = RedCoreVolMount();
356             }
357 
358             if( ret == 0 )
359             {
360                 /*  Increment the mount generation, invalidating file descriptors
361                  *  from previous mounts.  Note that while the generation numbers
362                  *  are stored in 16-bit values, we have less than 16-bits to store
363                  *  generations in the file descriptors, so we must wrap-around
364                  *  manually.
365                  */
366                 gauGeneration[ bVolNum ]++;
367 
368                 if( gauGeneration[ bVolNum ] > FD_GEN_MAX )
369                 {
370                     /*  Wrap-around to one, rather than zero.  The generation is
371                      *  stored in the top bits of the file descriptor, and doing
372                      *  this means that low numbers are never valid file
373                      *  descriptors.  This implements the requirement that 0, 1,
374                      *  and 2 are never valid file descriptors, thereby avoiding
375                      *  confusion with STDIN, STDOUT, and STDERR.
376                      */
377                     gauGeneration[ bVolNum ] = 1U;
378                 }
379             }
380 
381             PosixLeave();
382         }
383 
384         return PosixReturn( ret );
385     }
386 
387 
388 /** @brief Unmount a file system volume.
389  *
390  *  This function discards the in-memory state for the file system and marks it
391  *  as unmounted.  Subsequent attempts to access the volume will fail until the
392  *  volume is mounted again.
393  *
394  *  If unmount automatic transaction points are enabled, this function will
395  *  commit a transaction point prior to unmounting.  If unmount automatic
396  *  transaction points are disabled, this function will unmount without
397  *  transacting, effectively discarding the working state.
398  *
399  *  Before unmounting, this function will wait for any active file system
400  *  thread to complete by acquiring the FS mutex.  The volume will be marked as
401  *  unmounted before the FS mutex is released, so subsequent FS threads will
402  *  possibly block and then see an error when attempting to access a volume
403  *  which is unmounting or unmounted.  If the volume has open handles, the
404  *  unmount will fail.
405  *
406  *  An error is returned if the volume is already unmounted.
407  *
408  *  @param pszVolume    A path prefix identifying the volume to unmount.
409  *
410  *  @return On success, zero is returned.  On error, -1 is returned and
411  #red_errno is set appropriately.
412  *
413  *  <b>Errno values</b>
414  *  - #RED_EBUSY: There are still open handles for this file system volume.
415  *  - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized; or
416  *    the volume is already unmounted.
417  *  - #RED_EIO: I/O error during unmount automatic transaction point.
418  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
419  *  - #RED_EUSERS: Cannot become a file system user: too many users.
420  */
red_umount(const char * pszVolume)421     int32_t red_umount( const char * pszVolume )
422     {
423         REDSTATUS ret;
424 
425         ret = PosixEnter();
426 
427         if( ret == 0 )
428         {
429             uint8_t bVolNum;
430 
431             ret = RedPathSplit( pszVolume, &bVolNum, NULL );
432 
433             /*  The core will return success if the volume is already unmounted, so
434              *  check for that condition here to propagate the error.
435              */
436             if( ( ret == 0 ) && !gaRedVolume[ bVolNum ].fMounted )
437             {
438                 ret = -RED_EINVAL;
439             }
440 
441             if( ret == 0 )
442             {
443                 uint16_t uHandleIdx;
444 
445                 /*  Do not unmount the volume if it still has open handles.
446                  */
447                 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ )
448                 {
449                     const REDHANDLE * pHandle = &gaHandle[ uHandleIdx ];
450 
451                     if( ( pHandle->ulInode != INODE_INVALID ) && ( pHandle->bVolNum == bVolNum ) )
452                     {
453                         ret = -RED_EBUSY;
454                         break;
455                     }
456                 }
457             }
458 
459             #if REDCONF_VOLUME_COUNT > 1U
460                 if( ret == 0 )
461                 {
462                     ret = RedCoreVolSetCurrent( bVolNum );
463                 }
464             #endif
465 
466             if( ret == 0 )
467             {
468                 ret = RedCoreVolUnmount();
469             }
470 
471             PosixLeave();
472         }
473 
474         return PosixReturn( ret );
475     }
476 
477 
478     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FORMAT == 1 )
479 
480 /** @brief Format a file system volume.
481  *
482  *  Uses the statically defined volume configuration.  After calling this
483  *  function, the volume needs to be mounted -- see red_mount().
484  *
485  *  An error is returned if the volume is mounted.
486  *
487  *  @param pszVolume    A path prefix identifying the volume to format.
488  *
489  *  @return On success, zero is returned.  On error, -1 is returned and
490  #red_errno is set appropriately.
491  *
492  *  <b>Errno values</b>
493  *  - #RED_EBUSY: Volume is mounted.
494  *  - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized.
495  *  - #RED_EIO: I/O error formatting the volume.
496  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
497  *  - #RED_EUSERS: Cannot become a file system user: too many users.
498  */
red_format(const char * pszVolume)499         int32_t red_format( const char * pszVolume )
500         {
501             REDSTATUS ret;
502 
503             ret = PosixEnter();
504 
505             if( ret == 0 )
506             {
507                 uint8_t bVolNum;
508 
509                 ret = RedPathSplit( pszVolume, &bVolNum, NULL );
510 
511                 #if REDCONF_VOLUME_COUNT > 1U
512                     if( ret == 0 )
513                     {
514                         ret = RedCoreVolSetCurrent( bVolNum );
515                     }
516                 #endif
517 
518                 if( ret == 0 )
519                 {
520                     ret = RedCoreVolFormat();
521                 }
522 
523                 PosixLeave();
524             }
525 
526             return PosixReturn( ret );
527         }
528     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FORMAT == 1 ) */
529 
530 
531     #if REDCONF_READ_ONLY == 0
532 
533 /** @brief Commit a transaction point.
534  *
535  *  Reliance Edge is a transactional file system.  All modifications, of both
536  *  metadata and filedata, are initially working state.  A transaction point
537  *  is a process whereby the working state atomically becomes the committed
538  *  state, replacing the previous committed state.  Whenever Reliance Edge is
539  *  mounted, including after power loss, the state of the file system after
540  *  mount is the most recent committed state.  Nothing from the committed
541  *  state is ever missing, and nothing from the working state is ever included.
542  *
543  *  @param pszVolume    A path prefix identifying the volume to transact.
544  *
545  *  @return On success, zero is returned.  On error, -1 is returned and
546  #red_errno is set appropriately.
547  *
548  *  <b>Errno values</b>
549  *  - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`.
550  *  - #RED_EIO: I/O error during the transaction point.
551  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
552  *  - #RED_EUSERS: Cannot become a file system user: too many users.
553  */
red_transact(const char * pszVolume)554         int32_t red_transact( const char * pszVolume )
555         {
556             REDSTATUS ret;
557 
558             ret = PosixEnter();
559 
560             if( ret == 0 )
561             {
562                 uint8_t bVolNum;
563 
564                 ret = RedPathSplit( pszVolume, &bVolNum, NULL );
565 
566                 #if REDCONF_VOLUME_COUNT > 1U
567                     if( ret == 0 )
568                     {
569                         ret = RedCoreVolSetCurrent( bVolNum );
570                     }
571                 #endif
572 
573                 if( ret == 0 )
574                 {
575                     ret = RedCoreVolTransact();
576                 }
577 
578                 PosixLeave();
579             }
580 
581             return PosixReturn( ret );
582         }
583     #endif /* if REDCONF_READ_ONLY == 0 */
584 
585 
586     #if REDCONF_READ_ONLY == 0
587 
588 /** @brief Update the transaction mask.
589  *
590  *  The following events are available:
591  *
592  *  - #RED_TRANSACT_UMOUNT
593  *  - #RED_TRANSACT_CREAT
594  *  - #RED_TRANSACT_UNLINK
595  *  - #RED_TRANSACT_MKDIR
596  *  - #RED_TRANSACT_RENAME
597  *  - #RED_TRANSACT_LINK
598  *  - #RED_TRANSACT_CLOSE
599  *  - #RED_TRANSACT_WRITE
600  *  - #RED_TRANSACT_FSYNC
601  *  - #RED_TRANSACT_TRUNCATE
602  *  - #RED_TRANSACT_VOLFULL
603  *
604  *  The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all
605  *  automatic transaction events.  The #RED_TRANSACT_MASK macro is a bitmask
606  *  of all transaction flags, excluding those representing excluded
607  *  functionality.
608  *
609  *  Attempting to enable events for excluded functionality will result in an
610  *  error.
611  *
612  *  @param pszVolume    The path prefix of the volume whose transaction mask is
613  *                      being changed.
614  *  @param ulEventMask  A bitwise-OR'd mask of automatic transaction events to
615  *                      be set as the current transaction mode.
616  *
617  *  @return On success, zero is returned.  On error, -1 is returned and
618  #red_errno is set appropriately.
619  *
620  *  <b>Errno values</b>
621  *  - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
622  *    @p ulEventMask contains invalid bits.
623  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
624  *  - #RED_EUSERS: Cannot become a file system user: too many users.
625  */
red_settransmask(const char * pszVolume,uint32_t ulEventMask)626         int32_t red_settransmask( const char * pszVolume,
627                                   uint32_t ulEventMask )
628         {
629             REDSTATUS ret;
630 
631             ret = PosixEnter();
632 
633             if( ret == 0 )
634             {
635                 uint8_t bVolNum;
636 
637                 ret = RedPathSplit( pszVolume, &bVolNum, NULL );
638 
639                 #if REDCONF_VOLUME_COUNT > 1U
640                     if( ret == 0 )
641                     {
642                         ret = RedCoreVolSetCurrent( bVolNum );
643                     }
644                 #endif
645 
646                 if( ret == 0 )
647                 {
648                     ret = RedCoreTransMaskSet( ulEventMask );
649                 }
650 
651                 PosixLeave();
652             }
653 
654             return PosixReturn( ret );
655         }
656     #endif /* if REDCONF_READ_ONLY == 0 */
657 
658 
659 /** @brief Read the transaction mask.
660  *
661  *  If the volume is read-only, the returned event mask is always zero.
662  *
663  *  @param pszVolume    The path prefix of the volume whose transaction mask is
664  *                      being retrieved.
665  *  @param pulEventMask Populated with a bitwise-OR'd mask of automatic
666  *                      transaction events which represent the current
667  *                      transaction mode for the volume.
668  *
669  *  @return On success, zero is returned.  On error, -1 is returned and
670  #red_errno is set appropriately.
671  *
672  *  <b>Errno values</b>
673  *  - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
674  *    @p pulEventMask is `NULL`.
675  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
676  *  - #RED_EUSERS: Cannot become a file system user: too many users.
677  */
red_gettransmask(const char * pszVolume,uint32_t * pulEventMask)678     int32_t red_gettransmask( const char * pszVolume,
679                               uint32_t * pulEventMask )
680     {
681         REDSTATUS ret;
682 
683         ret = PosixEnter();
684 
685         if( ret == 0 )
686         {
687             uint8_t bVolNum;
688 
689             ret = RedPathSplit( pszVolume, &bVolNum, NULL );
690 
691             #if REDCONF_VOLUME_COUNT > 1U
692                 if( ret == 0 )
693                 {
694                     ret = RedCoreVolSetCurrent( bVolNum );
695                 }
696             #endif
697 
698             if( ret == 0 )
699             {
700                 ret = RedCoreTransMaskGet( pulEventMask );
701             }
702 
703             PosixLeave();
704         }
705 
706         return PosixReturn( ret );
707     }
708 
709 
710 /** @brief Query file system status information.
711  *
712  *  @p pszVolume should name a valid volume prefix or a valid root directory;
713  *  this differs from POSIX statvfs, where any existing file or directory is a
714  *  valid path.
715  *
716  *  @param pszVolume    The path prefix of the volume to query.
717  *  @param pStatvfs     The buffer to populate with volume information.
718  *
719  *  @return On success, zero is returned.  On error, -1 is returned and
720  #red_errno is set appropriately.
721  *
722  *  <b>Errno values</b>
723  *  - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
724  *    @p pStatvfs is `NULL`.
725  *  - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
726  *  - #RED_EUSERS: Cannot become a file system user: too many users.
727  */
red_statvfs(const char * pszVolume,REDSTATFS * pStatvfs)728     int32_t red_statvfs( const char * pszVolume,
729                          REDSTATFS * pStatvfs )
730     {
731         REDSTATUS ret;
732 
733         ret = PosixEnter();
734 
735         if( ret == 0 )
736         {
737             uint8_t bVolNum;
738 
739             ret = RedPathSplit( pszVolume, &bVolNum, NULL );
740 
741             #if REDCONF_VOLUME_COUNT > 1U
742                 if( ret == 0 )
743                 {
744                     ret = RedCoreVolSetCurrent( bVolNum );
745                 }
746             #endif
747 
748             if( ret == 0 )
749             {
750                 ret = RedCoreVolStat( pStatvfs );
751             }
752 
753             PosixLeave();
754         }
755 
756         return PosixReturn( ret );
757     }
758 
759 
760 /** @brief Open a file or directory.
761  *
762  *  Exactly one file access mode must be specified:
763  *
764  *  - #RED_O_RDONLY: Open for reading only.
765  *  - #RED_O_WRONLY: Open for writing only.
766  *  - #RED_O_RDWR: Open for reading and writing.
767  *
768  *  Directories can only be opened with `RED_O_RDONLY`.
769  *
770  *  The following flags may also be used:
771  *
772  *  - #RED_O_APPEND: Set the file offset to the end-of-file prior to each
773  *    write.
774  *  - #RED_O_CREAT: Create the named file if it does not exist.
775  *  - #RED_O_EXCL: In combination with `RED_O_CREAT`, return an error if the
776  *    path already exists.
777  *  - #RED_O_TRUNC: Truncate the opened file to size zero.  Only supported when
778  #REDCONF_API_POSIX_FTRUNCATE is true.
779  *
780  #RED_O_CREAT, #RED_O_EXCL, and #RED_O_TRUNC are invalid with #RED_O_RDONLY.
781  #RED_O_EXCL is invalid without #RED_O_CREAT.
782  *
783  *  If the volume is read-only, #RED_O_RDONLY is the only valid open flag; use
784  *  of any other flag will result in an error.
785  *
786  *  If #RED_O_TRUNC frees data which is in the committed state, it will not
787  *  return to free space until after a transaction point.
788  *
789  *  The returned file descriptor must later be closed with red_close().
790  *
791  *  Unlike POSIX open, there is no optional third argument for the permissions
792  *  (which Reliance Edge does not use) and other open flags (like `O_SYNC`) are
793  *  not supported.
794  *
795  *  @param pszPath      The path to the file or directory.
796  *  @param ulOpenMode   The open flags (mask of `RED_O_` values).
797  *
798  *  @return On success, a nonnegative file descriptor is returned.  On error,
799  *          -1 is returned and #red_errno is set appropriately.
800  *
801  *  <b>Errno values</b>
802  *  - #RED_EEXIST: Using #RED_O_CREAT and #RED_O_EXCL, and the indicated path
803  *    already exists.
804  *  - #RED_EINVAL: @p ulOpenMode is invalid; or @p pszPath is `NULL`; or the
805  *    volume containing the path is not mounted.
806  *  - #RED_EIO: A disk I/O error occurred.
807  *  - #RED_EISDIR: The path names a directory and @p ulOpenMode includes
808  #RED_O_WRONLY or #RED_O_RDWR.
809  *  - #RED_EMFILE: There are no available file descriptors.
810  *  - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
811  #REDCONF_NAME_MAX.
812  *  - #RED_ENFILE: Attempting to create a file but the file system has used all
813  *    available inode slots.
814  *  - #RED_ENOENT: #RED_O_CREAT is not set and the named file does not exist; or
815  #RED_O_CREAT is set and the parent directory does not exist; or the
816  *    volume does not exist; or the @p pszPath argument, after removing the
817  *    volume prefix, points to an empty string.
818  *  - #RED_ENOSPC: The file does not exist and #RED_O_CREAT was specified, but
819  *    there is insufficient free space to expand the directory or to create the
820  *    new file.
821  *  - #RED_ENOTDIR: A component of the prefix in @p pszPath does not name a
822  *    directory.
823  *  - #RED_EROFS: The path resides on a read-only file system and a write
824  *    operation was requested.
825  *  - #RED_EUSERS: Cannot become a file system user: too many users.
826  */
red_open(const char * pszPath,uint32_t ulOpenMode)827     int32_t red_open( const char * pszPath,
828                       uint32_t ulOpenMode )
829     {
830         int32_t iFildes = -1; /* Init'd to quiet warnings. */
831         REDSTATUS ret;
832 
833         #if REDCONF_READ_ONLY == 1
834             if( ulOpenMode != RED_O_RDONLY )
835             {
836                 ret = -RED_EROFS;
837             }
838         #else
839             if( ( ulOpenMode != ( ulOpenMode & RED_O_MASK ) ) ||
840                 ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_WRONLY | RED_O_RDWR ) ) == 0U ) ||
841                 ( ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) && ( ( ulOpenMode & ( RED_O_WRONLY | RED_O_RDWR ) ) != 0U ) ) ||
842                 ( ( ( ulOpenMode & RED_O_WRONLY ) != 0U ) && ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_RDWR ) ) != 0U ) ) ||
843                 ( ( ( ulOpenMode & RED_O_RDWR ) != 0U ) && ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_WRONLY ) ) != 0U ) ) ||
844                 ( ( ( ulOpenMode & ( RED_O_TRUNC | RED_O_CREAT | RED_O_EXCL ) ) != 0U ) && ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) ) ||
845                 ( ( ( ulOpenMode & RED_O_EXCL ) != 0U ) && ( ( ulOpenMode & RED_O_CREAT ) == 0U ) ) )
846             {
847                 ret = -RED_EINVAL;
848             }
849 
850             #if REDCONF_API_POSIX_FTRUNCATE == 0
851                 else if( ( ulOpenMode & RED_O_TRUNC ) != 0U )
852                 {
853                     ret = -RED_EINVAL;
854                 }
855             #endif
856         #endif /* if REDCONF_READ_ONLY == 1 */
857         else
858         {
859             ret = PosixEnter();
860         }
861 
862         if( ret == 0 )
863         {
864             ret = FildesOpen( pszPath, ulOpenMode, FTYPE_EITHER, &iFildes );
865 
866             PosixLeave();
867         }
868 
869         if( ret != 0 )
870         {
871             iFildes = PosixReturn( ret );
872         }
873 
874         return iFildes;
875     }
876 
877 
878     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_UNLINK == 1 )
879 
880 /** @brief Delete a file or directory.
881  *
882  *  The given name is deleted and the link count of the corresponding inode is
883  *  decremented.  If the link count falls to zero (no remaining hard links),
884  *  the inode will be deleted.
885  *
886  *  Unlike POSIX unlink, deleting a file or directory with open handles (file
887  *  descriptors or directory streams) will fail with an #RED_EBUSY error.  This
888  *  only applies when deleting an inode with a link count of one; if a file has
889  *  multiple names (hard links), all but the last name may be deleted even if
890  *  the file is open.
891  *
892  *  If the path names a directory which is not empty, the unlink will fail.
893  *
894  *  If the deletion frees data in the committed state, it will not return to
895  *  free space until after a transaction point.
896  *
897  *  Unlike POSIX unlink, this function can fail when the disk is full.  To fix
898  *  this, transact and try again: Reliance Edge guarantees that it is possible
899  *  to delete at least one file or directory after a transaction point.  If disk
900  *  full automatic transactions are enabled, this will happen automatically.
901  *
902  *  @param pszPath  The path of the file or directory to delete.
903  *
904  *  @return On success, zero is returned.  On error, -1 is returned and
905  #red_errno is set appropriately.
906  *
907  *  <b>Errno values</b>
908  *  - #RED_EBUSY: @p pszPath points to an inode with open handles and a link
909  *    count of one.
910  *  - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
911  *    not mounted.
912  *  - #RED_EIO: A disk I/O error occurred.
913  *  - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
914  #REDCONF_NAME_MAX.
915  *  - #RED_ENOENT: The path does not name an existing file; or the @p pszPath
916  *    argument, after removing the volume prefix, points to an empty string.
917  *  - #RED_ENOTDIR: A component of the path prefix is not a directory.
918  *  - #RED_ENOTEMPTY: The path names a directory which is not empty.
919  *  - #RED_ENOSPC: The file system does not have enough space to modify the
920  *    parent directory to perform the deletion.
921  *  - #RED_EUSERS: Cannot become a file system user: too many users.
922  */
red_unlink(const char * pszPath)923         int32_t red_unlink( const char * pszPath )
924         {
925             REDSTATUS ret;
926 
927             ret = PosixEnter();
928 
929             if( ret == 0 )
930             {
931                 ret = UnlinkSub( pszPath, FTYPE_EITHER );
932 
933                 PosixLeave();
934             }
935 
936             return PosixReturn( ret );
937         }
938     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_UNLINK == 1 ) */
939 
940 
941     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_MKDIR == 1 )
942 
943 /** @brief Create a new directory.
944  *
945  *  Unlike POSIX mkdir, this function has no second argument for the
946  *  permissions (which Reliance Edge does not use).
947  *
948  *  @param pszPath  The name and location of the directory to create.
949  *
950  *  @return On success, zero is returned.  On error, -1 is returned and
951  #red_errno is set appropriately.
952  *
953  *  <b>Errno values</b>
954  *  - #RED_EEXIST: @p pszPath points to an existing file or directory.
955  *  - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
956  *    not mounted.
957  *  - #RED_EIO: A disk I/O error occurred.
958  *  - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
959  #REDCONF_NAME_MAX.
960  *  - #RED_ENOENT: A component of the path prefix does not name an existing
961  *    directory; or the @p pszPath argument, after removing the volume prefix,
962  *    points to an empty string.
963  *  - #RED_ENOSPC: The file system does not have enough space for the new
964  *    directory or to extend the parent directory of the new directory.
965  *  - #RED_ENOTDIR: A component of the path prefix is not a directory.
966  *  - #RED_EROFS: The parent directory resides on a read-only file system.
967  *  - #RED_EUSERS: Cannot become a file system user: too many users.
968  */
red_mkdir(const char * pszPath)969         int32_t red_mkdir( const char * pszPath )
970         {
971             REDSTATUS ret;
972 
973             ret = PosixEnter();
974 
975             if( ret == 0 )
976             {
977                 const char * pszLocalPath;
978                 uint8_t bVolNum;
979 
980                 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath );
981 
982                 #if REDCONF_VOLUME_COUNT > 1U
983                     if( ret == 0 )
984                     {
985                         ret = RedCoreVolSetCurrent( bVolNum );
986                     }
987                 #endif
988 
989                 if( ret == 0 )
990                 {
991                     const char * pszName;
992                     uint32_t ulPInode;
993 
994                     ret = RedPathToName( pszLocalPath, &ulPInode, &pszName );
995 
996                     if( ret == 0 )
997                     {
998                         uint32_t ulInode;
999 
1000                         ret = RedCoreCreate( ulPInode, pszName, true, &ulInode );
1001                     }
1002                 }
1003 
1004                 PosixLeave();
1005             }
1006 
1007             return PosixReturn( ret );
1008         }
1009     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_MKDIR == 1 ) */
1010 
1011 
1012     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RMDIR == 1 )
1013 
1014 /** @brief Delete a directory.
1015  *
1016  *  The given directory name is deleted and the corresponding directory inode
1017  *  will be deleted.
1018  *
1019  *  Unlike POSIX rmdir, deleting a directory with open handles (file
1020  *  descriptors or directory streams) will fail with an #RED_EBUSY error.
1021  *
1022  *  If the path names a directory which is not empty, the deletion will fail.
1023  *  If the path names the root directory of a file system volume, the deletion
1024  *  will fail.
1025  *
1026  *  If the path names a regular file, the deletion will fail.  This provides
1027  *  type checking and may be useful in cases where an application knows the
1028  *  path to be deleted should name a directory.
1029  *
1030  *  If the deletion frees data in the committed state, it will not return to
1031  *  free space until after a transaction point.
1032  *
1033  *  Unlike POSIX rmdir, this function can fail when the disk is full.  To fix
1034  *  this, transact and try again: Reliance Edge guarantees that it is possible
1035  *  to delete at least one file or directory after a transaction point.  If disk
1036  *  full automatic transactions are enabled, this will happen automatically.
1037  *
1038  *  @param pszPath  The path of the directory to delete.
1039  *
1040  *  @return On success, zero is returned.  On error, -1 is returned and
1041  #red_errno is set appropriately.
1042  *
1043  *  <b>Errno values</b>
1044  *  - #RED_EBUSY: @p pszPath points to a directory with open handles.
1045  *  - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
1046  *    not mounted.
1047  *  - #RED_EIO: A disk I/O error occurred.
1048  *  - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
1049  #REDCONF_NAME_MAX.
1050  *  - #RED_ENOENT: The path does not name an existing directory; or the
1051  *    @p pszPath argument, after removing the volume prefix, points to an empty
1052  *    string.
1053  *  - #RED_ENOTDIR: A component of the path is not a directory.
1054  *  - #RED_ENOTEMPTY: The path names a directory which is not empty.
1055  *  - #RED_ENOSPC: The file system does not have enough space to modify the
1056  *    parent directory to perform the deletion.
1057  *  - #RED_EROFS: The directory to be removed resides on a read-only file
1058  *    system.
1059  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1060  */
red_rmdir(const char * pszPath)1061         int32_t red_rmdir( const char * pszPath )
1062         {
1063             REDSTATUS ret;
1064 
1065             ret = PosixEnter();
1066 
1067             if( ret == 0 )
1068             {
1069                 ret = UnlinkSub( pszPath, FTYPE_DIR );
1070 
1071                 PosixLeave();
1072             }
1073 
1074             return PosixReturn( ret );
1075         }
1076     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RMDIR == 1 ) */
1077 
1078 
1079     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RENAME == 1 )
1080 
1081 /** @brief Rename a file or directory.
1082  *
1083  *  Both paths must reside on the same file system volume.  Attempting to use
1084  *  this API to move a file to a different volume will result in an error.
1085  *
1086  *  If @p pszNewPath names an existing file or directory, the behavior depends
1087  *  on the configuration.  If #REDCONF_RENAME_ATOMIC is false, and if the
1088  *  destination name exists, this function always fails and sets #red_errno to
1089  #RED_EEXIST.  This behavior is contrary to POSIX.
1090  *
1091  *  If #REDCONF_RENAME_ATOMIC is true, and if the new name exists, then in one
1092  *  atomic operation, @p pszNewPath is unlinked and @p pszOldPath is renamed to
1093  *  @p pszNewPath.  Both @p pszNewPath and @p pszOldPath must be of the same
1094  *  type (both files or both directories).  As with red_unlink(), if
1095  *  @p pszNewPath is a directory, it must be empty.  The major exception to this
1096  *  behavior is that if both @p pszOldPath and @p pszNewPath are links to the
1097  *  same inode, then the rename does nothing and both names continue to exist.
1098  *  Unlike POSIX rename, if @p pszNewPath points to an inode with a link count
1099  *  of one and open handles (file descriptors or directory streams), the
1100  *  rename will fail with #RED_EBUSY.
1101  *
1102  *  If the rename deletes the old destination, it may free data in the
1103  *  committed state, which will not return to free space until after a
1104  *  transaction point.  Similarly, if the deleted inode was part of the
1105  *  committed state, the inode slot will not be available until after a
1106  *  transaction point.
1107  *
1108  *  @param pszOldPath   The path of the file or directory to rename.
1109  *  @param pszNewPath   The new name and location after the rename.
1110  *
1111  *  @return On success, zero is returned.  On error, -1 is returned and
1112  #red_errno is set appropriately.
1113  *
1114  *  <b>Errno values</b>
1115  *  - #RED_EBUSY: #REDCONF_RENAME_ATOMIC is true and @p pszNewPath points to an
1116  *    inode with open handles and a link count of one.
1117  *  - #RED_EEXIST: #REDCONF_RENAME_ATOMIC is false and @p pszNewPath exists.
1118  *  - #RED_EINVAL: @p pszOldPath is `NULL`; or @p pszNewPath is `NULL`; or the
1119  *    volume containing the path is not mounted.
1120  *  - #RED_EIO: A disk I/O error occurred.
1121  *  - #RED_EISDIR: The @p pszNewPath argument names a directory and the
1122  *    @p pszOldPath argument names a non-directory.
1123  *  - #RED_ENAMETOOLONG: The length of a component of either @p pszOldPath or
1124  *    @p pszNewPath is longer than #REDCONF_NAME_MAX.
1125  *  - #RED_ENOENT: The link named by @p pszOldPath does not name an existing
1126  *    entry; or either @p pszOldPath or @p pszNewPath, after removing the volume
1127  *    prefix, point to an empty string.
1128  *  - #RED_ENOTDIR: A component of either path prefix is not a directory; or
1129  *    @p pszOldPath names a directory and @p pszNewPath names a file.
1130  *  - #RED_ENOTEMPTY: The path named by @p pszNewPath is a directory which is
1131  *    not empty.
1132  *  - #RED_ENOSPC: The file system does not have enough space to extend the
1133  *    directory that would contain @p pszNewPath.
1134  *  - #RED_EROFS: The directory to be removed resides on a read-only file
1135  *    system.
1136  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1137  *  - #RED_EXDEV: @p pszOldPath and @p pszNewPath are on different file system
1138  *    volumes.
1139  */
red_rename(const char * pszOldPath,const char * pszNewPath)1140         int32_t red_rename( const char * pszOldPath,
1141                             const char * pszNewPath )
1142         {
1143             REDSTATUS ret;
1144 
1145             ret = PosixEnter();
1146 
1147             if( ret == 0 )
1148             {
1149                 const char * pszOldLocalPath;
1150                 uint8_t bOldVolNum;
1151 
1152                 ret = RedPathSplit( pszOldPath, &bOldVolNum, &pszOldLocalPath );
1153 
1154                 if( ret == 0 )
1155                 {
1156                     const char * pszNewLocalPath;
1157                     uint8_t bNewVolNum;
1158 
1159                     ret = RedPathSplit( pszNewPath, &bNewVolNum, &pszNewLocalPath );
1160 
1161                     if( ( ret == 0 ) && ( bOldVolNum != bNewVolNum ) )
1162                     {
1163                         ret = -RED_EXDEV;
1164                     }
1165 
1166                     #if REDCONF_VOLUME_COUNT > 1U
1167                         if( ret == 0 )
1168                         {
1169                             ret = RedCoreVolSetCurrent( bOldVolNum );
1170                         }
1171                     #endif
1172 
1173                     if( ret == 0 )
1174                     {
1175                         const char * pszOldName;
1176                         uint32_t ulOldPInode;
1177 
1178                         ret = RedPathToName( pszOldLocalPath, &ulOldPInode, &pszOldName );
1179 
1180                         if( ret == 0 )
1181                         {
1182                             const char * pszNewName;
1183                             uint32_t ulNewPInode;
1184 
1185                             ret = RedPathToName( pszNewLocalPath, &ulNewPInode, &pszNewName );
1186 
1187                             #if REDCONF_RENAME_ATOMIC == 1
1188                                 if( ret == 0 )
1189                                 {
1190                                     uint32_t ulDestInode;
1191 
1192                                     ret = RedCoreLookup( ulNewPInode, pszNewName, &ulDestInode );
1193 
1194                                     if( ret == 0 )
1195                                     {
1196                                         ret = InodeUnlinkCheck( ulDestInode );
1197                                     }
1198                                     else if( ret == -RED_ENOENT )
1199                                     {
1200                                         ret = 0;
1201                                     }
1202                                     else
1203                                     {
1204                                         /*  Unexpected error, nothing to do.
1205                                          */
1206                                     }
1207                                 }
1208                             #endif /* if REDCONF_RENAME_ATOMIC == 1 */
1209 
1210                             if( ret == 0 )
1211                             {
1212                                 ret = RedCoreRename( ulOldPInode, pszOldName, ulNewPInode, pszNewName );
1213                             }
1214                         }
1215                     }
1216                 }
1217 
1218                 PosixLeave();
1219             }
1220 
1221             return PosixReturn( ret );
1222         }
1223     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RENAME == 1 ) */
1224 
1225 
1226     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_LINK == 1 )
1227 
1228 /** @brief Create a hard link.
1229  *
1230  *  This creates an additional name (link) for the file named by @p pszPath.
1231  *  The new name refers to the same file with the same contents.  If a name is
1232  *  deleted, but the underlying file has other names, the file continues to
1233  *  exist.  The link count (accessible via red_fstat()) indicates the number of
1234  *  names that a file has.  All of a file's names are on equal footing: there
1235  *  is nothing special about the original name.
1236  *
1237  *  If @p pszPath names a directory, the operation will fail.
1238  *
1239  *  @param pszPath      The path indicating the inode for the new link.
1240  *  @param pszHardLink  The name and location for the new link.
1241  *
1242  *  @return On success, zero is returned.  On error, -1 is returned and
1243  #red_errno is set appropriately.
1244  *
1245  *  <b>Errno values</b>
1246  *  - #RED_EEXIST: @p pszHardLink resolves to an existing file.
1247  *  - #RED_EINVAL: @p pszPath or @p pszHardLink is `NULL`; or the volume
1248  *    containing the paths is not mounted.
1249  *  - #RED_EIO: A disk I/O error occurred.
1250  *  - #RED_EMLINK: Creating the link would exceed the maximum link count of the
1251  *    inode named by @p pszPath.
1252  *  - #RED_ENAMETOOLONG: The length of a component of either @p pszPath or
1253  *    @p pszHardLink is longer than #REDCONF_NAME_MAX.
1254  *  - #RED_ENOENT: A component of either path prefix does not exist; or the file
1255  *    named by @p pszPath does not exist; or either @p pszPath or
1256  *    @p pszHardLink, after removing the volume prefix, point to an empty
1257  *    string.
1258  *  - #RED_ENOSPC: There is insufficient free space to expand the directory that
1259  *    would contain the link.
1260  *  - #RED_ENOTDIR: A component of either path prefix is not a directory.
1261  *  - #RED_EPERM: The @p pszPath argument names a directory.
1262  *  - #RED_EROFS: The requested link requires writing in a directory on a
1263  *    read-only file system.
1264  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1265  *  - #RED_EXDEV: @p pszPath and @p pszHardLink are on different file system
1266  *    volumes.
1267  */
red_link(const char * pszPath,const char * pszHardLink)1268         int32_t red_link( const char * pszPath,
1269                           const char * pszHardLink )
1270         {
1271             REDSTATUS ret;
1272 
1273             ret = PosixEnter();
1274 
1275             if( ret == 0 )
1276             {
1277                 const char * pszLocalPath;
1278                 uint8_t bVolNum;
1279 
1280                 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath );
1281 
1282                 if( ret == 0 )
1283                 {
1284                     const char * pszLinkLocalPath;
1285                     uint8_t bLinkVolNum;
1286 
1287                     ret = RedPathSplit( pszHardLink, &bLinkVolNum, &pszLinkLocalPath );
1288 
1289                     if( ( ret == 0 ) && ( bVolNum != bLinkVolNum ) )
1290                     {
1291                         ret = -RED_EXDEV;
1292                     }
1293 
1294                     #if REDCONF_VOLUME_COUNT > 1U
1295                         if( ret == 0 )
1296                         {
1297                             ret = RedCoreVolSetCurrent( bVolNum );
1298                         }
1299                     #endif
1300 
1301                     if( ret == 0 )
1302                     {
1303                         uint32_t ulInode;
1304 
1305                         ret = RedPathLookup( pszLocalPath, &ulInode );
1306 
1307                         if( ret == 0 )
1308                         {
1309                             const char * pszLinkName;
1310                             uint32_t ulLinkPInode;
1311 
1312                             ret = RedPathToName( pszLinkLocalPath, &ulLinkPInode, &pszLinkName );
1313 
1314                             if( ret == 0 )
1315                             {
1316                                 ret = RedCoreLink( ulLinkPInode, pszLinkName, ulInode );
1317                             }
1318                         }
1319                     }
1320                 }
1321 
1322                 PosixLeave();
1323             }
1324 
1325             return PosixReturn( ret );
1326         }
1327     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_LINK == 1 ) */
1328 
1329 
1330 /** @brief Close a file descriptor.
1331  *
1332  *  @param iFildes  The file descriptor to close.
1333  *
1334  *  @return On success, zero is returned.  On error, -1 is returned and
1335  #red_errno is set appropriately.
1336  *
1337  *  <b>Errno values</b>
1338  *  - #RED_EBADF: @p iFildes is not a valid file descriptor.
1339  *  - #RED_EIO: A disk I/O error occurred.
1340  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1341  */
red_close(int32_t iFildes)1342     int32_t red_close( int32_t iFildes )
1343     {
1344         REDSTATUS ret;
1345 
1346         ret = PosixEnter();
1347 
1348         if( ret == 0 )
1349         {
1350             ret = FildesClose( iFildes );
1351 
1352             PosixLeave();
1353         }
1354 
1355         return PosixReturn( ret );
1356     }
1357 
1358 
1359 /** @brief Read from an open file.
1360  *
1361  *  The read takes place at the file offset associated with @p iFildes and
1362  *  advances the file offset by the number of bytes actually read.
1363  *
1364  *  Data which has not yet been written, but which is before the end-of-file
1365  *  (sparse data), will read as zeroes.  A short read -- where the number of
1366  *  bytes read is less than requested -- indicates that the requested read was
1367  *  partially or, if zero bytes were read, entirely beyond the end-of-file.
1368  *
1369  *  @param iFildes  The file descriptor from which to read.
1370  *  @param pBuffer  The buffer to populate with data read.  Must be at least
1371  *                  @p ulLength bytes in size.
1372  *  @param ulLength Number of bytes to attempt to read.
1373  *
1374  *  @return On success, returns a nonnegative value indicating the number of
1375  *          bytes actually read.  On error, -1 is returned and #red_errno is
1376  *          set appropriately.
1377  *
1378  *  <b>Errno values</b>
1379  *  - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
1380  *    for reading.
1381  *  - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and
1382  *    cannot be returned properly.
1383  *  - #RED_EIO: A disk I/O error occurred.
1384  *  - #RED_EISDIR: The @p iFildes is a file descriptor for a directory.
1385  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1386  */
red_read(int32_t iFildes,void * pBuffer,uint32_t ulLength)1387     int32_t red_read( int32_t iFildes,
1388                       void * pBuffer,
1389                       uint32_t ulLength )
1390     {
1391         uint32_t ulLenRead = 0U;
1392         REDSTATUS ret;
1393         int32_t iReturn;
1394 
1395         if( ulLength > ( uint32_t ) INT32_MAX )
1396         {
1397             ret = -RED_EINVAL;
1398         }
1399         else
1400         {
1401             ret = PosixEnter();
1402         }
1403 
1404         if( ret == 0 )
1405         {
1406             REDHANDLE * pHandle;
1407 
1408             ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle );
1409 
1410             if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_READABLE ) == 0U ) )
1411             {
1412                 ret = -RED_EBADF;
1413             }
1414 
1415             #if REDCONF_VOLUME_COUNT > 1U
1416                 if( ret == 0 )
1417                 {
1418                     ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1419                 }
1420             #endif
1421 
1422             if( ret == 0 )
1423             {
1424                 ulLenRead = ulLength;
1425                 ret = RedCoreFileRead( pHandle->ulInode, pHandle->ullOffset, &ulLenRead, pBuffer );
1426             }
1427 
1428             if( ret == 0 )
1429             {
1430                 REDASSERT( ulLenRead <= ulLength );
1431 
1432                 pHandle->ullOffset += ulLenRead;
1433             }
1434 
1435             PosixLeave();
1436         }
1437 
1438         if( ret == 0 )
1439         {
1440             iReturn = ( int32_t ) ulLenRead;
1441         }
1442         else
1443         {
1444             iReturn = PosixReturn( ret );
1445         }
1446 
1447         return iReturn;
1448     }
1449 
1450 
1451     #if REDCONF_READ_ONLY == 0
1452 
1453 /** @brief Write to an open file.
1454  *
1455  *  The write takes place at the file offset associated with @p iFildes and
1456  *  advances the file offset by the number of bytes actually written.
1457  *  Alternatively, if @p iFildes was opened with #RED_O_APPEND, the file offset
1458  *  is set to the end-of-file before the write begins, and likewise advances by
1459  *  the number of bytes actually written.
1460  *
1461  *  A short write -- where the number of bytes written is less than requested
1462  *  -- indicates either that the file system ran out of space but was still
1463  *  able to write some of the request; or that the request would have caused
1464  *  the file to exceed the maximum file size, but some of the data could be
1465  *  written prior to the file size limit.
1466  *
1467  *  If an error is returned (-1), either none of the data was written or a
1468  *  critical error occurred (like an I/O error) and the file system volume will
1469  *  be read-only.
1470  *
1471  *  @param iFildes  The file descriptor to write to.
1472  *  @param pBuffer  The buffer containing the data to be written.  Must be at
1473  *                  least @p ulLength bytes in size.
1474  *  @param ulLength Number of bytes to attempt to write.
1475  *
1476  *  @return On success, returns a nonnegative value indicating the number of
1477  *          bytes actually written.  On error, -1 is returned and #red_errno is
1478  *          set appropriately.
1479  *
1480  *  <b>Errno values</b>
1481  *  - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
1482  *    for writing.  This includes the case where the file descriptor is for a
1483  *    directory.
1484  *  - #RED_EFBIG: No data can be written to the current file offset since the
1485  *    resulting file size would exceed the maximum file size.
1486  *  - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and
1487  *    cannot be returned properly.
1488  *  - #RED_EIO: A disk I/O error occurred.
1489  *  - #RED_ENOSPC: No data can be written because there is insufficient free
1490  *    space.
1491  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1492  */
red_write(int32_t iFildes,const void * pBuffer,uint32_t ulLength)1493         int32_t red_write( int32_t iFildes,
1494                            const void * pBuffer,
1495                            uint32_t ulLength )
1496         {
1497             uint32_t ulLenWrote = 0U;
1498             REDSTATUS ret;
1499             int32_t iReturn;
1500 
1501             if( ulLength > ( uint32_t ) INT32_MAX )
1502             {
1503                 ret = -RED_EINVAL;
1504             }
1505             else
1506             {
1507                 ret = PosixEnter();
1508             }
1509 
1510             if( ret == 0 )
1511             {
1512                 REDHANDLE * pHandle;
1513 
1514                 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle );
1515 
1516                 if( ret == -RED_EISDIR )
1517                 {
1518                     /*  POSIX says that if a file descriptor is not writable, the
1519                      *  errno should be -RED_EBADF.  Directory file descriptors are
1520                      *  never writable, and unlike for read(), the spec does not
1521                      *  list -RED_EISDIR as an allowed errno.  Therefore -RED_EBADF
1522                      *  takes precedence.
1523                      */
1524                     ret = -RED_EBADF;
1525                 }
1526 
1527                 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_WRITEABLE ) == 0U ) )
1528                 {
1529                     ret = -RED_EBADF;
1530                 }
1531 
1532                 #if REDCONF_VOLUME_COUNT > 1U
1533                     if( ret == 0 )
1534                     {
1535                         ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1536                     }
1537                 #endif
1538 
1539                 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_APPENDING ) != 0U ) )
1540                 {
1541                     REDSTAT s;
1542 
1543                     ret = RedCoreStat( pHandle->ulInode, &s );
1544 
1545                     if( ret == 0 )
1546                     {
1547                         pHandle->ullOffset = s.st_size;
1548                     }
1549                 }
1550 
1551                 if( ret == 0 )
1552                 {
1553                     ulLenWrote = ulLength;
1554                     ret = RedCoreFileWrite( pHandle->ulInode, pHandle->ullOffset, &ulLenWrote, pBuffer );
1555                 }
1556 
1557                 if( ret == 0 )
1558                 {
1559                     REDASSERT( ulLenWrote <= ulLength );
1560 
1561                     pHandle->ullOffset += ulLenWrote;
1562                 }
1563 
1564                 PosixLeave();
1565             }
1566 
1567             if( ret == 0 )
1568             {
1569                 iReturn = ( int32_t ) ulLenWrote;
1570             }
1571             else
1572             {
1573                 iReturn = PosixReturn( ret );
1574             }
1575 
1576             return iReturn;
1577         }
1578     #endif /* if REDCONF_READ_ONLY == 0 */
1579 
1580 
1581     #if REDCONF_READ_ONLY == 0
1582 
1583 /** @brief Synchronizes changes to a file.
1584  *
1585  *  Commits all changes associated with a file or directory (including file
1586  *  data, directory contents, and metadata) to permanent storage.  This
1587  *  function will not return until the operation is complete.
1588  *
1589  *  In the current implementation, this function has global effect.  All dirty
1590  *  buffers are flushed and a transaction point is committed.  Fsyncing one
1591  *  file effectively fsyncs all files.
1592  *
1593  *  If fsync automatic transactions have been disabled, this function does
1594  *  nothing and returns success.  In the current implementation, this is the
1595  *  only real difference between this function and red_transact(): this
1596  *  function can be configured to do nothing, whereas red_transact() is
1597  *  unconditional.
1598  *
1599  *  Applications written for portability should avoid assuming red_fsync()
1600  *  effects all files, and use red_fsync() on each file that needs to be
1601  *  synchronized.
1602  *
1603  *  Passing read-only file descriptors to this function is permitted.
1604  *
1605  *  @param iFildes  The file descriptor to synchronize.
1606  *
1607  *  @return On success, zero is returned.  On error, -1 is returned and
1608  #red_errno is set appropriately.
1609  *
1610  *  <b>Errno values</b>
1611  *  - #RED_EBADF: The @p iFildes argument is not a valid file descriptor.
1612  *  - #RED_EIO: A disk I/O error occurred.
1613  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1614  */
red_fsync(int32_t iFildes)1615         int32_t red_fsync( int32_t iFildes )
1616         {
1617             REDSTATUS ret;
1618 
1619             ret = PosixEnter();
1620 
1621             if( ret == 0 )
1622             {
1623                 REDHANDLE * pHandle;
1624 
1625                 ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle );
1626 
1627                 #if REDCONF_VOLUME_COUNT > 1U
1628                     if( ret == 0 )
1629                     {
1630                         ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1631                     }
1632                 #endif
1633 
1634                 /*  No core event for fsync, so this transaction flag needs to be
1635                  *  implemented here.
1636                  */
1637                 if( ret == 0 )
1638                 {
1639                     uint32_t ulTransMask;
1640 
1641                     ret = RedCoreTransMaskGet( &ulTransMask );
1642 
1643                     if( ( ret == 0 ) && ( ( ulTransMask & RED_TRANSACT_FSYNC ) != 0U ) )
1644                     {
1645                         ret = RedCoreVolTransact();
1646                     }
1647                 }
1648 
1649                 PosixLeave();
1650             }
1651 
1652             return PosixReturn( ret );
1653         }
1654     #endif /* if REDCONF_READ_ONLY == 0 */
1655 
1656 
1657 /** @brief Move the read/write file offset.
1658  *
1659  *  The file offset of the @p iFildes file descriptor is set to @p llOffset,
1660  *  relative to some starting position.  The available positions are:
1661  *
1662  *  - ::RED_SEEK_SET Seek from the start of the file.  In other words,
1663  *    @p llOffset becomes the new file offset.
1664  *  - ::RED_SEEK_CUR Seek from the current file offset.  In other words,
1665  *    @p llOffset is added to the current file offset.
1666  *  - ::RED_SEEK_END Seek from the end-of-file.  In other words, the new file
1667  *    offset is the file size plus @p llOffset.
1668  *
1669  *  Since @p llOffset is signed (can be negative), it is possible to seek
1670  *  backward with ::RED_SEEK_CUR or ::RED_SEEK_END.
1671  *
1672  *  It is permitted to seek beyond the end-of-file; this does not increase the
1673  *  file size (a subsequent red_write() call would).
1674  *
1675  *  Unlike POSIX lseek, this function cannot be used with directory file
1676  *  descriptors.
1677  *
1678  *  @param iFildes  The file descriptor whose offset is to be updated.
1679  *  @param llOffset The new file offset, relative to @p whence.
1680  *  @param whence   The location from which @p llOffset should be applied.
1681  *
1682  *  @return On success, returns the new file position, measured in bytes from
1683  *          the beginning of the file.  On error, -1 is returned and #red_errno
1684  *          is set appropriately.
1685  *
1686  *  <b>Errno values</b>
1687  *  - #RED_EBADF: The @p iFildes argument is not an open file descriptor.
1688  *  - #RED_EINVAL: @p whence is not a valid `RED_SEEK_` value; or the resulting
1689  *    file offset would be negative or beyond the maximum file size.
1690  *  - #RED_EIO: A disk I/O error occurred.
1691  *  - #RED_EISDIR: The @p iFildes argument is a file descriptor for a directory.
1692  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1693  */
red_lseek(int32_t iFildes,int64_t llOffset,REDWHENCE whence)1694     int64_t red_lseek( int32_t iFildes,
1695                        int64_t llOffset,
1696                        REDWHENCE whence )
1697     {
1698         REDSTATUS ret;
1699         int64_t llReturn = -1; /* Init'd to quiet warnings. */
1700 
1701         ret = PosixEnter();
1702 
1703         if( ret == 0 )
1704         {
1705             int64_t llFrom = 0; /* Init'd to quiet warnings. */
1706             REDHANDLE * pHandle;
1707 
1708             /*  Unlike POSIX, we disallow lseek() on directory handles.
1709              */
1710             ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle );
1711 
1712             #if REDCONF_VOLUME_COUNT > 1U
1713                 if( ret == 0 )
1714                 {
1715                     ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1716                 }
1717             #endif
1718 
1719             if( ret == 0 )
1720             {
1721                 switch( whence )
1722                 {
1723                     /*  Seek from the beginning of the file.
1724                      */
1725                     case RED_SEEK_SET:
1726                         llFrom = 0;
1727                         break;
1728 
1729                     /*  Seek from the current file offset.
1730                      */
1731                     case RED_SEEK_CUR:
1732                         REDASSERT( pHandle->ullOffset <= ( uint64_t ) INT64_MAX );
1733                         llFrom = ( int64_t ) pHandle->ullOffset;
1734                         break;
1735 
1736                     /*  Seek from the end of the file.
1737                      */
1738                     case RED_SEEK_END:
1739                        {
1740                            REDSTAT s;
1741 
1742                            ret = RedCoreStat( pHandle->ulInode, &s );
1743 
1744                            if( ret == 0 )
1745                            {
1746                                REDASSERT( s.st_size <= ( uint64_t ) INT64_MAX );
1747                                llFrom = ( int64_t ) s.st_size;
1748                            }
1749 
1750                            break;
1751                        }
1752 
1753                     default:
1754                         ret = -RED_EINVAL;
1755                         break;
1756                 }
1757             }
1758 
1759             if( ret == 0 )
1760             {
1761                 REDASSERT( llFrom >= 0 );
1762 
1763                 /*  Avoid signed integer overflow from llFrom + llOffset with large
1764                  *  values of llOffset and nonzero llFrom values.  Underflow isn't
1765                  *  possible since llFrom is nonnegative.
1766                  */
1767                 if( ( llOffset > 0 ) && ( ( ( uint64_t ) llFrom + ( uint64_t ) llOffset ) > ( uint64_t ) INT64_MAX ) )
1768                 {
1769                     ret = -RED_EINVAL;
1770                 }
1771                 else
1772                 {
1773                     int64_t llNewOffset = llFrom + llOffset;
1774 
1775                     if( ( llNewOffset < 0 ) || ( ( uint64_t ) llNewOffset > gpRedVolume->ullMaxInodeSize ) )
1776                     {
1777                         /*  Invalid file offset.
1778                          */
1779                         ret = -RED_EINVAL;
1780                     }
1781                     else
1782                     {
1783                         pHandle->ullOffset = ( uint64_t ) llNewOffset;
1784                         llReturn = llNewOffset;
1785                     }
1786                 }
1787             }
1788 
1789             PosixLeave();
1790         }
1791 
1792         if( ret != 0 )
1793         {
1794             llReturn = PosixReturn( ret );
1795         }
1796 
1797         return llReturn;
1798     }
1799 
1800 
1801     #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 )
1802 
1803 /** @brief Truncate a file to a specified length.
1804  *
1805  *  Allows the file size to be increased, decreased, or to remain the same.  If
1806  *  the file size is increased, the new area is sparse (will read as zeroes).
1807  *  If the file size is decreased, the data beyond the new end-of-file will
1808  *  return to free space once it is no longer part of the committed state
1809  *  (either immediately or after the next transaction point).
1810  *
1811  *  The value of the file offset is not modified by this function.
1812  *
1813  *  Unlike POSIX ftruncate, this function can fail when the disk is full if
1814  *  @p ullSize is non-zero.  If decreasing the file size, this can be fixed by
1815  *  transacting and trying again: Reliance Edge guarantees that it is possible
1816  *  to perform a truncate of at least one file that decreases the file size
1817  *  after a transaction point.  If disk full transactions are enabled, this will
1818  *  happen automatically.
1819  *
1820  *  @param iFildes  The file descriptor of the file to truncate.
1821  *  @param ullSize  The new size of the file.
1822  *
1823  *  @return On success, zero is returned.  On error, -1 is returned and
1824  #red_errno is set appropriately.
1825  *
1826  *  <b>Errno values</b>
1827  *  - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
1828  *    for writing.  This includes the case where the file descriptor is for a
1829  *    directory.
1830  *  - #RED_EFBIG: @p ullSize exceeds the maximum file size.
1831  *  - #RED_EIO: A disk I/O error occurred.
1832  *  - #RED_ENOSPC: Insufficient free space to perform the truncate.
1833  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1834  */
red_ftruncate(int32_t iFildes,uint64_t ullSize)1835         int32_t red_ftruncate( int32_t iFildes,
1836                                uint64_t ullSize )
1837         {
1838             REDSTATUS ret;
1839 
1840             ret = PosixEnter();
1841 
1842             if( ret == 0 )
1843             {
1844                 REDHANDLE * pHandle;
1845 
1846                 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle );
1847 
1848                 if( ret == -RED_EISDIR )
1849                 {
1850                     /*  Similar to red_write() (see comment there), the RED_EBADF error
1851                      *  for a non-writable file descriptor takes precedence.
1852                      */
1853                     ret = -RED_EBADF;
1854                 }
1855 
1856                 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_WRITEABLE ) == 0U ) )
1857                 {
1858                     ret = -RED_EBADF;
1859                 }
1860 
1861                 #if REDCONF_VOLUME_COUNT > 1U
1862                     if( ret == 0 )
1863                     {
1864                         ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1865                     }
1866                 #endif
1867 
1868                 if( ret == 0 )
1869                 {
1870                     ret = RedCoreFileTruncate( pHandle->ulInode, ullSize );
1871                 }
1872 
1873                 PosixLeave();
1874             }
1875 
1876             return PosixReturn( ret );
1877         }
1878     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 ) */
1879 
1880 
1881 /** @brief Get the status of a file or directory.
1882  *
1883  *  See the ::REDSTAT type for the details of the information returned.
1884  *
1885  *  @param iFildes  An open file descriptor for the file whose information is
1886  *                  to be retrieved.
1887  *  @param pStat    Pointer to a ::REDSTAT buffer to populate.
1888  *
1889  *  @return On success, zero is returned.  On error, -1 is returned and
1890  #red_errno is set appropriately.
1891  *
1892  *  <b>Errno values</b>
1893  *  - #RED_EBADF: The @p iFildes argument is not a valid file descriptor.
1894  *  - #RED_EINVAL: @p pStat is `NULL`.
1895  *  - #RED_EIO: A disk I/O error occurred.
1896  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1897  */
red_fstat(int32_t iFildes,REDSTAT * pStat)1898     int32_t red_fstat( int32_t iFildes,
1899                        REDSTAT * pStat )
1900     {
1901         REDSTATUS ret;
1902 
1903         ret = PosixEnter();
1904 
1905         if( ret == 0 )
1906         {
1907             REDHANDLE * pHandle;
1908 
1909             ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle );
1910 
1911             #if REDCONF_VOLUME_COUNT > 1U
1912                 if( ret == 0 )
1913                 {
1914                     ret = RedCoreVolSetCurrent( pHandle->bVolNum );
1915                 }
1916             #endif
1917 
1918             if( ret == 0 )
1919             {
1920                 ret = RedCoreStat( pHandle->ulInode, pStat );
1921             }
1922 
1923             PosixLeave();
1924         }
1925 
1926         return PosixReturn( ret );
1927     }
1928 
1929 
1930     #if REDCONF_API_POSIX_READDIR == 1
1931 
1932 /** @brief Open a directory stream for reading.
1933  *
1934  *  @param pszPath  The path of the directory to open.
1935  *
1936  *  @return On success, returns a pointer to a ::REDDIR object that can be used
1937  *          with red_readdir() and red_closedir().  On error, returns `NULL`
1938  *          and #red_errno is set appropriately.
1939  *
1940  *  <b>Errno values</b>
1941  *  - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
1942  *    not mounted.
1943  *  - #RED_EIO: A disk I/O error occurred.
1944  *  - #RED_ENOENT: A component of @p pszPath does not exist; or the @p pszPath
1945  *    argument, after removing the volume prefix, points to an empty string.
1946  *  - #RED_ENOTDIR: A component of @p pszPath is a not a directory.
1947  *  - #RED_EMFILE: There are no available file descriptors.
1948  *  - #RED_EUSERS: Cannot become a file system user: too many users.
1949  */
red_opendir(const char * pszPath)1950         REDDIR * red_opendir( const char * pszPath )
1951         {
1952             int32_t iFildes;
1953             REDSTATUS ret;
1954             REDDIR * pDir = NULL;
1955 
1956             ret = PosixEnter();
1957 
1958             if( ret == 0 )
1959             {
1960                 ret = FildesOpen( pszPath, RED_O_RDONLY, FTYPE_DIR, &iFildes );
1961 
1962                 if( ret == 0 )
1963                 {
1964                     uint16_t uHandleIdx;
1965 
1966                     FildesUnpack( iFildes, &uHandleIdx, NULL, NULL );
1967                     pDir = &gaHandle[ uHandleIdx ];
1968                 }
1969 
1970                 PosixLeave();
1971             }
1972 
1973             REDASSERT( ( pDir == NULL ) == ( ret != 0 ) );
1974 
1975             if( pDir == NULL )
1976             {
1977                 red_errno = -ret;
1978             }
1979 
1980             return pDir;
1981         }
1982 
1983 
1984 /** @brief Read from a directory stream.
1985  *
1986  *  The ::REDDIRENT pointer returned by this function will be overwritten by
1987  *  subsequent calls on the same @p pDir.  Calls with other ::REDDIR objects
1988  *  will *not* modify the returned ::REDDIRENT.
1989  *
1990  *  If files are added to the directory after it is opened, the new files may
1991  *  or may not be returned by this function.  If files are deleted, the deleted
1992  *  files will not be returned.
1993  *
1994  *  This function (like its POSIX equivalent) returns `NULL` in two cases: on
1995  *  error and when the end of the directory is reached.  To distinguish between
1996  *  these two cases, the application should set #red_errno to zero before
1997  *  calling this function, and if `NULL` is returned, check if #red_errno is
1998  *  still zero.  If it is, the end of the directory was reached; otherwise,
1999  *  there was an error.
2000  *
2001  *  @param pDirStream   The directory stream to read from.
2002  *
2003  *  @return On success, returns a pointer to a ::REDDIRENT object which is
2004  *          populated with directory entry information read from the directory.
2005  *          On error, returns `NULL` and #red_errno is set appropriately.  If at
2006  *          the end of the directory, returns `NULL` but #red_errno is not
2007  *          modified.
2008  *
2009  *  <b>Errno values</b>
2010  *  - #RED_EBADF: @p pDirStream is not an open directory stream.
2011  *  - #RED_EIO: A disk I/O error occurred.
2012  *  - #RED_EUSERS: Cannot become a file system user: too many users.
2013  */
red_readdir(REDDIR * pDirStream)2014         REDDIRENT * red_readdir( REDDIR * pDirStream )
2015         {
2016             REDSTATUS ret;
2017             REDDIRENT * pDirEnt = NULL;
2018 
2019             ret = PosixEnter();
2020 
2021             if( ret == 0 )
2022             {
2023                 if( !DirStreamIsValid( pDirStream ) )
2024                 {
2025                     ret = -RED_EBADF;
2026                 }
2027 
2028                 #if REDCONF_VOLUME_COUNT > 1U
2029                     else
2030                     {
2031                         ret = RedCoreVolSetCurrent( pDirStream->bVolNum );
2032                     }
2033                 #endif
2034 
2035                 if( ret == 0 )
2036                 {
2037                     uint32_t ulDirPosition;
2038 
2039                     /*  To save memory, the directory position is stored in the same
2040                      *  location as the file offset.  This would be a bit cleaner using
2041                      *  a union, but MISRA-C:2012 Rule 19.2 disallows unions.
2042                      */
2043                     REDASSERT( pDirStream->ullOffset <= UINT32_MAX );
2044                     ulDirPosition = ( uint32_t ) pDirStream->ullOffset;
2045 
2046                     ret = RedCoreDirRead( pDirStream->ulInode, &ulDirPosition, pDirStream->dirent.d_name, &pDirStream->dirent.d_ino );
2047 
2048                     pDirStream->ullOffset = ulDirPosition;
2049 
2050                     if( ret == 0 )
2051                     {
2052                         /*  POSIX extension: return stat information with the dirent.
2053                          */
2054                         ret = RedCoreStat( pDirStream->dirent.d_ino, &pDirStream->dirent.d_stat );
2055 
2056                         if( ret == 0 )
2057                         {
2058                             pDirEnt = &pDirStream->dirent;
2059                         }
2060                     }
2061                     else if( ret == -RED_ENOENT )
2062                     {
2063                         /*  Reached the end of the directory; return NULL but do not set
2064                          *  errno.
2065                          */
2066                         ret = 0;
2067                     }
2068                     else
2069                     {
2070                         /*  Miscellaneous error; return NULL and set errno (done below).
2071                          */
2072                     }
2073                 }
2074 
2075                 PosixLeave();
2076             }
2077 
2078             if( ret != 0 )
2079             {
2080                 REDASSERT( pDirEnt == NULL );
2081 
2082                 red_errno = -ret;
2083             }
2084 
2085             return pDirEnt;
2086         }
2087 
2088 
2089 /** @brief Rewind a directory stream to read it from the beginning.
2090  *
2091  *  Similar to closing the directory object and opening it again, but without
2092  *  the need for the path.
2093  *
2094  *  Since this function (like its POSIX equivalent) cannot return an error,
2095  *  it takes no action in error conditions, such as when @p pDirStream is
2096  *  invalid.
2097  *
2098  *  @param pDirStream   The directory stream to rewind.
2099  */
red_rewinddir(REDDIR * pDirStream)2100         void red_rewinddir( REDDIR * pDirStream )
2101         {
2102             if( PosixEnter() == 0 )
2103             {
2104                 if( DirStreamIsValid( pDirStream ) )
2105                 {
2106                     pDirStream->ullOffset = 0U;
2107                 }
2108 
2109                 PosixLeave();
2110             }
2111         }
2112 
2113 
2114 /** @brief Close a directory stream.
2115  *
2116  *  After calling this function, @p pDirStream should no longer be used.
2117  *
2118  *  @param pDirStream   The directory stream to close.
2119  *
2120  *  @return On success, zero is returned.  On error, -1 is returned and
2121  #red_errno is set appropriately.
2122  *
2123  *  <b>Errno values</b>
2124  *  - #RED_EBADF: @p pDirStream is not an open directory stream.
2125  *  - #RED_EUSERS: Cannot become a file system user: too many users.
2126  */
red_closedir(REDDIR * pDirStream)2127         int32_t red_closedir( REDDIR * pDirStream )
2128         {
2129             REDSTATUS ret;
2130 
2131             ret = PosixEnter();
2132 
2133             if( ret == 0 )
2134             {
2135                 if( DirStreamIsValid( pDirStream ) )
2136                 {
2137                     /*  Mark this handle as unused.
2138                      */
2139                     pDirStream->ulInode = INODE_INVALID;
2140                 }
2141                 else
2142                 {
2143                     ret = -RED_EBADF;
2144                 }
2145 
2146                 PosixLeave();
2147             }
2148 
2149             return PosixReturn( ret );
2150         }
2151     #endif /* REDCONF_API_POSIX_READDIR */
2152 
2153 
2154 /** @brief Pointer to where the last file system error (errno) is stored.
2155  *
2156  *  This function is intended to be used via the #red_errno macro, or a similar
2157  *  user-defined macro, that can be used both as an lvalue (writable) and an
2158  *  rvalue (readable).
2159  *
2160  *  Under normal circumstances, the errno for each task is stored in a
2161  *  different location.  Applications do not need to worry about one task
2162  *  obliterating an error value that another task needed to read.  This task
2163  *  errno for is initially zero.  When one of the POSIX-like APIs returns an
2164  *  indication of error, the location for the calling task will be populated
2165  *  with the error value.
2166  *
2167  *  In some circumstances, this function will return a pointer to a global
2168  *  errno location which is shared by multiple tasks.  If the calling task is
2169  *  not registered as a file system user and all of the task slots are full,
2170  *  there can be no task-specific errno, so the global pointer is returned.
2171  *  Likewise, if the file system driver is uninitialized, there are no
2172  *  registered file system users and this function always returns the pointer
2173  *  to the global errno.  Under these circumstances, multiple tasks
2174  *  manipulating errno could be problematic.
2175  *
2176  *  This function never returns `NULL` under any circumstances.  The #red_errno
2177  *  macro unconditionally dereferences the return value from this function, so
2178  *  returning `NULL` could result in a fault.
2179  *
2180  *  @return Pointer to where the errno value is stored for this task.
2181  */
red_errnoptr(void)2182     REDSTATUS * red_errnoptr( void )
2183     {
2184         /*  The global errno value, used in single-task configurations and when the
2185          *  caller is not (and cannot become) a file system user (which includes
2186          *  when the driver is uninitialized).
2187          */
2188         static REDSTATUS iGlobalErrno = 0;
2189 
2190         #if REDCONF_TASK_COUNT == 1U
2191             return &iGlobalErrno;
2192         #else
2193             REDSTATUS * piErrno;
2194 
2195             if( gfPosixInited )
2196             {
2197                 uint32_t ulTaskId = RedOsTaskId();
2198                 uint32_t ulIdx;
2199 
2200                 REDASSERT( ulTaskId != 0U );
2201 
2202                 /*  If this task has used the file system before, it will already have
2203                  *  a task slot, which includes the task-specific errno.
2204                  */
2205                 RedOsMutexAcquire();
2206 
2207                 for( ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++ )
2208                 {
2209                     if( gaTask[ ulIdx ].ulTaskId == ulTaskId )
2210                     {
2211                         break;
2212                     }
2213                 }
2214 
2215                 RedOsMutexRelease();
2216 
2217                 if( ulIdx == REDCONF_TASK_COUNT )
2218                 {
2219                     REDSTATUS ret;
2220 
2221                     /*  This task is not a file system user, so try to register it as
2222                      *  one.  This FS mutex must be held in order to register.
2223                      */
2224                     RedOsMutexAcquire();
2225 
2226                     ret = TaskRegister( &ulIdx );
2227 
2228                     RedOsMutexRelease();
2229 
2230                     if( ret == 0 )
2231                     {
2232                         REDASSERT( gaTask[ ulIdx ].ulTaskId == RedOsTaskId() );
2233                         REDASSERT( gaTask[ ulIdx ].iErrno == 0 );
2234 
2235                         piErrno = &gaTask[ ulIdx ].iErrno;
2236                     }
2237                     else
2238                     {
2239                         /*  Unable to register; use the global errno.
2240                          */
2241                         piErrno = &iGlobalErrno;
2242                     }
2243                 }
2244                 else
2245                 {
2246                     piErrno = &gaTask[ ulIdx ].iErrno;
2247                 }
2248             }
2249             else
2250             {
2251                 /*  There are no registered file system tasks when the driver is
2252                  *  uninitialized, so use the global errno.
2253                  */
2254                 piErrno = &iGlobalErrno;
2255             }
2256 
2257             /*  This function is not allowed to return NULL.
2258              */
2259             REDASSERT( piErrno != NULL );
2260             return piErrno;
2261         #endif /* if REDCONF_TASK_COUNT == 1U */
2262     }
2263 /** @} */
2264 
2265 /*-------------------------------------------------------------------
2266  *   Helper Functions
2267  *  -------------------------------------------------------------------*/
2268 
2269     #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
2270 
2271 /** @brief Remove a link to a file or directory.
2272  *
2273  *  If the link count becomes zero, the file or directory is deleted.
2274  *
2275  *  @param pszPath  Path of the link to remove.
2276  *  @param type     The expected type of the path: file, directory, or either.
2277  *                  An error is returned if the expected type is file or
2278  *                  directory and does not match the path.
2279  *
2280  *  @return A negated ::REDSTATUS code indicating the operation result.
2281  *
2282  *  @retval -RED_EBUSY          @p pszPath points to an inode with open handles
2283  *                              and a link count of one.
2284  *  @retval -RED_EINVAL         @p pszPath is `NULL`; or the volume containing
2285  *                              the path is not mounted.
2286  *  @retval -RED_EIO            A disk I/O error occurred.
2287  *  @retval -RED_EISDIR         @p type is ::FTYPE_FILE and the path names a
2288  *                              directory.
2289  *  @retval -RED_ENAMETOOLONG   @p pszName is too long.
2290  *  @retval -RED_ENOENT         The path does not name an existing file; or
2291  *                              @p pszPath, after removing the volume prefix,
2292  *                              points to an empty string.
2293  *  @retval -RED_ENOTDIR        @p type is ::FTYPE_DIR and the path does not
2294  *                              name a directory.
2295  *  @retval -RED_ENOTEMPTY      @p pszPath is a directory which is not empty.
2296  *  @retval -RED_ENOSPC         The file system does not have enough space to
2297  *                              modify the parent directory to perform the
2298  *                              deletion.
2299  */
UnlinkSub(const char * pszPath,FTYPE type)2300         static REDSTATUS UnlinkSub( const char * pszPath,
2301                                     FTYPE type )
2302         {
2303             uint8_t bVolNum;
2304             const char * pszLocalPath;
2305             REDSTATUS ret;
2306 
2307             ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath );
2308 
2309             #if REDCONF_VOLUME_COUNT > 1U
2310                 if( ret == 0 )
2311                 {
2312                     ret = RedCoreVolSetCurrent( bVolNum );
2313                 }
2314             #endif
2315 
2316             if( ret == 0 )
2317             {
2318                 const char * pszName;
2319                 uint32_t ulPInode;
2320 
2321                 ret = RedPathToName( pszLocalPath, &ulPInode, &pszName );
2322 
2323                 if( ret == 0 )
2324                 {
2325                     uint32_t ulInode;
2326 
2327                     ret = RedCoreLookup( ulPInode, pszName, &ulInode );
2328 
2329                     /*  ModeTypeCheck() always passes when the type is FTYPE_EITHER, so
2330                      *  skip stat'ing the inode in that case.
2331                      */
2332                     if( ( ret == 0 ) && ( type != FTYPE_EITHER ) )
2333                     {
2334                         REDSTAT InodeStat;
2335 
2336                         ret = RedCoreStat( ulInode, &InodeStat );
2337 
2338                         if( ret == 0 )
2339                         {
2340                             ret = ModeTypeCheck( InodeStat.st_mode, type );
2341                         }
2342                     }
2343 
2344                     if( ret == 0 )
2345                     {
2346                         ret = InodeUnlinkCheck( ulInode );
2347                     }
2348 
2349                     if( ret == 0 )
2350                     {
2351                         ret = RedCoreUnlink( ulPInode, pszName );
2352                     }
2353                 }
2354             }
2355 
2356             return ret;
2357         }
2358     #endif /* (REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) */
2359 
2360 
2361 /** @brief Get a file descriptor for a path.
2362  *
2363  *  @param pszPath      Path to a file to open.
2364  *  @param ulOpenMode   The RED_O_* flags the descriptor is opened with.
2365  *  @param type         Indicates the expected descriptor type: file, directory,
2366  *                      or either.
2367  *  @param piFildes     On successful return, populated with the file
2368  *                      descriptor.
2369  *
2370  *  @return A negated ::REDSTATUS code indicating the operation result.
2371  *
2372  *  @retval 0                   Operation was successful.
2373  *  @retval -RED_EINVAL         @p piFildes is `NULL`; or @p pszPath is `NULL`;
2374  *                              or the volume is not mounted.
2375  *  @retval -RED_EMFILE         There are no available handles.
2376  *  @retval -RED_EEXIST         Using #RED_O_CREAT and #RED_O_EXCL, and the
2377  *                              indicated path already exists.
2378  *  @retval -RED_EISDIR         The path names a directory and @p ulOpenMode
2379  *                              includes #RED_O_WRONLY or #RED_O_RDWR.
2380  *  @retval -RED_ENOENT         #RED_O_CREAT is not set and the named file does
2381  *                              not exist; or #RED_O_CREAT is set and the parent
2382  *                              directory does not exist; or the volume does not
2383  *                              exist; or the @p pszPath argument, after
2384  *                              removing the volume prefix, points to an empty
2385  *                              string.
2386  *  @retval -RED_EIO            A disk I/O error occurred.
2387  *  @retval -RED_ENAMETOOLONG   The length of a component of @p pszPath is
2388  *                              longer than #REDCONF_NAME_MAX.
2389  *  @retval -RED_ENFILE         Attempting to create a file but the file system
2390  *                              has used all available inode slots.
2391  *  @retval -RED_ENOSPC         The file does not exist and #RED_O_CREAT was
2392  *                              specified, but there is insufficient free space
2393  *                              to expand the directory or to create the new
2394  *                              file.
2395  *  @retval -RED_ENOTDIR        A component of the prefix in @p pszPath does not
2396  *                              name a directory.
2397  *  @retval -RED_EROFS          The path resides on a read-only file system and
2398  *                              a write operation was requested.
2399  */
FildesOpen(const char * pszPath,uint32_t ulOpenMode,FTYPE type,int32_t * piFildes)2400     static REDSTATUS FildesOpen( const char * pszPath,
2401                                  uint32_t ulOpenMode,
2402                                  FTYPE type,
2403                                  int32_t * piFildes )
2404     {
2405         uint8_t bVolNum;
2406         const char * pszLocalPath;
2407         REDSTATUS ret;
2408 
2409         ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath );
2410 
2411         if( ret == 0 )
2412         {
2413             if( piFildes == NULL )
2414             {
2415                 ret = -RED_EINVAL;
2416             }
2417 
2418             #if REDCONF_READ_ONLY == 0
2419                 else if( gaRedVolume[ bVolNum ].fReadOnly && ( ulOpenMode != RED_O_RDONLY ) )
2420                 {
2421                     ret = -RED_EROFS;
2422                 }
2423             #endif
2424             else
2425             {
2426                 uint16_t uHandleIdx;
2427                 REDHANDLE * pHandle = NULL;
2428 
2429                 /*  Search for an unused handle.
2430                  */
2431                 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ )
2432                 {
2433                     if( gaHandle[ uHandleIdx ].ulInode == INODE_INVALID )
2434                     {
2435                         pHandle = &gaHandle[ uHandleIdx ];
2436                         break;
2437                     }
2438                 }
2439 
2440                 /*  Error if all the handles are in use.
2441                  */
2442                 if( pHandle == NULL )
2443                 {
2444                     ret = -RED_EMFILE;
2445                 }
2446                 else
2447                 {
2448                     bool fCreated = false;
2449                     uint16_t uMode = 0U;
2450                     uint32_t ulInode = 0U; /* Init'd to quiet warnings. */
2451 
2452                     #if REDCONF_VOLUME_COUNT > 1U
2453                         ret = RedCoreVolSetCurrent( bVolNum );
2454 
2455                         if( ret == 0 )
2456                     #endif
2457                     {
2458                         #if REDCONF_READ_ONLY == 0
2459                             if( ( ulOpenMode & RED_O_CREAT ) != 0U )
2460                             {
2461                                 uint32_t ulPInode;
2462                                 const char * pszName;
2463 
2464                                 ret = RedPathToName( pszLocalPath, &ulPInode, &pszName );
2465 
2466                                 if( ret == 0 )
2467                                 {
2468                                     ret = RedCoreCreate( ulPInode, pszName, false, &ulInode );
2469 
2470                                     if( ret == 0 )
2471                                     {
2472                                         fCreated = true;
2473                                     }
2474                                     else if( ( ret == -RED_EEXIST ) && ( ( ulOpenMode & RED_O_EXCL ) == 0U ) )
2475                                     {
2476                                         /*  If the path already exists and that's OK,
2477                                          *  lookup its inode number.
2478                                          */
2479                                         ret = RedCoreLookup( ulPInode, pszName, &ulInode );
2480                                     }
2481                                     else
2482                                     {
2483                                         /*  No action, just propagate the error.
2484                                          */
2485                                     }
2486                                 }
2487                             }
2488                             else
2489                         #endif /* if REDCONF_READ_ONLY == 0 */
2490                         {
2491                             ret = RedPathLookup( pszLocalPath, &ulInode );
2492                         }
2493                     }
2494 
2495                     /*  If we created the inode, none of the below stuff is
2496                      *  necessary.  This is important from an error handling
2497                      *  perspective -- we do not need code to delete the created
2498                      *  inode on error.
2499                      */
2500                     if( !fCreated )
2501                     {
2502                         if( ret == 0 )
2503                         {
2504                             REDSTAT s;
2505 
2506                             ret = RedCoreStat( ulInode, &s );
2507 
2508                             if( ret == 0 )
2509                             {
2510                                 uMode = s.st_mode;
2511                             }
2512                         }
2513 
2514                         /*  Error if the inode is not of the expected type.
2515                          */
2516                         if( ret == 0 )
2517                         {
2518                             ret = ModeTypeCheck( uMode, type );
2519                         }
2520 
2521                         /*  Directories must always be opened with O_RDONLY.
2522                          */
2523                         if( ( ret == 0 ) && RED_S_ISDIR( uMode ) && ( ( ulOpenMode & RED_O_RDONLY ) == 0U ) )
2524                         {
2525                             ret = -RED_EISDIR;
2526                         }
2527 
2528                         #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 )
2529                             if( ( ret == 0 ) && ( ( ulOpenMode & RED_O_TRUNC ) != 0U ) )
2530                             {
2531                                 ret = RedCoreFileTruncate( ulInode, UINT64_SUFFIX( 0 ) );
2532                             }
2533                         #endif
2534                     }
2535 
2536                     if( ret == 0 )
2537                     {
2538                         int32_t iFildes;
2539 
2540                         RedMemSet( pHandle, 0U, sizeof( *pHandle ) );
2541 
2542                         /*  Populate this handle, marking it as in use.
2543                          */
2544                         pHandle->ulInode = ulInode;
2545                         pHandle->bVolNum = bVolNum;
2546 
2547                         if( RED_S_ISDIR( uMode ) )
2548                         {
2549                             pHandle->bFlags |= HFLAG_DIRECTORY;
2550                         }
2551 
2552                         if( ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) || ( ( ulOpenMode & RED_O_RDWR ) != 0U ) )
2553                         {
2554                             pHandle->bFlags |= HFLAG_READABLE;
2555                         }
2556 
2557                         #if REDCONF_READ_ONLY == 0
2558                             if( ( ( ulOpenMode & RED_O_WRONLY ) != 0U ) || ( ( ulOpenMode & RED_O_RDWR ) != 0U ) )
2559                             {
2560                                 pHandle->bFlags |= HFLAG_WRITEABLE;
2561                             }
2562 
2563                             if( ( ulOpenMode & RED_O_APPEND ) != 0U )
2564                             {
2565                                 pHandle->bFlags |= HFLAG_APPENDING;
2566                             }
2567                         #endif
2568 
2569                         iFildes = FildesPack( uHandleIdx, bVolNum );
2570 
2571                         if( iFildes == -1 )
2572                         {
2573                             /*  It should be impossible to get here, unless there
2574                              *  is memory corruption.
2575                              */
2576                             REDERROR();
2577                             ret = -RED_EFUBAR;
2578                         }
2579                         else
2580                         {
2581                             *piFildes = iFildes;
2582                         }
2583                     }
2584                 }
2585             }
2586         }
2587 
2588         return ret;
2589     }
2590 
2591 
2592 /** @brief Close a file descriptor.
2593  *
2594  *  @param iFildes  The file descriptor to close.
2595  *
2596  *  @return A negated ::REDSTATUS code indicating the operation result.
2597  *
2598  *  @retval 0           Operation was successful.
2599  *  @retval -RED_EBADF  @p iFildes is not a valid file descriptor.
2600  *  @retval -RED_EIO    A disk I/O error occurred.
2601  */
FildesClose(int32_t iFildes)2602     static REDSTATUS FildesClose( int32_t iFildes )
2603     {
2604         REDHANDLE * pHandle;
2605         REDSTATUS ret;
2606 
2607         ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle );
2608 
2609         #if REDCONF_READ_ONLY == 0
2610             #if REDCONF_VOLUME_COUNT > 1U
2611                 if( ret == 0 )
2612                 {
2613                     ret = RedCoreVolSetCurrent( pHandle->bVolNum );
2614                 }
2615             #endif
2616 
2617             /*  No core event for close, so this transaction flag needs to be
2618              *  implemented here.
2619              */
2620             if( ret == 0 )
2621             {
2622                 uint32_t ulTransMask;
2623 
2624                 ret = RedCoreTransMaskGet( &ulTransMask );
2625 
2626                 if( ( ret == 0 ) && ( ( ulTransMask & RED_TRANSACT_CLOSE ) != 0U ) )
2627                 {
2628                     ret = RedCoreVolTransact();
2629                 }
2630             }
2631         #endif /* if REDCONF_READ_ONLY == 0 */
2632 
2633         if( ret == 0 )
2634         {
2635             /*  Mark this handle as unused.
2636              */
2637             pHandle->ulInode = INODE_INVALID;
2638         }
2639 
2640         return ret;
2641     }
2642 
2643 
2644 /** @brief Convert a file descriptor into a handle pointer.
2645  *
2646  *  Also validates the file descriptor.
2647  *
2648  *  @param iFildes  The file descriptor for which to get a handle.
2649  *  @param expectedType The expected type of the file descriptor: ::FTYPE_DIR,
2650  *                      ::FTYPE_FILE, or ::FTYPE_EITHER.
2651  *  @param ppHandle     On successful return, populated with a pointer to the
2652  *                      handle associated with @p iFildes.
2653  *
2654  *  @return A negated ::REDSTATUS code indicating the operation result.
2655  *
2656  *  @retval 0               Operation was successful.
2657  *  @retval -RED_EBADF      @p iFildes is not a valid file descriptor.
2658  *  @retval -RED_EINVAL     @p ppHandle is `NULL`.
2659  *  @retval -RED_EISDIR     Expected a file, but the file descriptor is for a
2660  *                          directory.
2661  *  @retval -RED_ENOTDIR    Expected a directory, but the file descriptor is for
2662  *                          a file.
2663  */
FildesToHandle(int32_t iFildes,FTYPE expectedType,REDHANDLE ** ppHandle)2664     static REDSTATUS FildesToHandle( int32_t iFildes,
2665                                      FTYPE expectedType,
2666                                      REDHANDLE ** ppHandle )
2667     {
2668         REDSTATUS ret;
2669 
2670         if( ppHandle == NULL )
2671         {
2672             REDERROR();
2673             ret = -RED_EINVAL;
2674         }
2675         else if( iFildes < FD_MIN )
2676         {
2677             ret = -RED_EBADF;
2678         }
2679         else
2680         {
2681             uint16_t uHandleIdx;
2682             uint8_t bVolNum;
2683             uint16_t uGeneration;
2684 
2685             FildesUnpack( iFildes, &uHandleIdx, &bVolNum, &uGeneration );
2686 
2687             if( ( uHandleIdx >= REDCONF_HANDLE_COUNT ) ||
2688                 ( bVolNum >= REDCONF_VOLUME_COUNT ) ||
2689                 ( gaHandle[ uHandleIdx ].ulInode == INODE_INVALID ) ||
2690                 ( gaHandle[ uHandleIdx ].bVolNum != bVolNum ) ||
2691                 ( gauGeneration[ bVolNum ] != uGeneration ) )
2692             {
2693                 ret = -RED_EBADF;
2694             }
2695             else if( ( expectedType == FTYPE_FILE ) && ( ( gaHandle[ uHandleIdx ].bFlags & HFLAG_DIRECTORY ) != 0U ) )
2696             {
2697                 ret = -RED_EISDIR;
2698             }
2699             else if( ( expectedType == FTYPE_DIR ) && ( ( gaHandle[ uHandleIdx ].bFlags & HFLAG_DIRECTORY ) == 0U ) )
2700             {
2701                 ret = -RED_ENOTDIR;
2702             }
2703             else
2704             {
2705                 *ppHandle = &gaHandle[ uHandleIdx ];
2706                 ret = 0;
2707             }
2708         }
2709 
2710         return ret;
2711     }
2712 
2713 
2714 /** @brief Pack a file descriptor.
2715  *
2716  *  @param uHandleIdx   The index of the file handle that will be associated
2717  *                      with this file descriptor.
2718  *  @param bVolNum      The volume which contains the file or directory this
2719  *                      file descriptor was opened against.
2720  *
2721  *  @return The packed file descriptor.
2722  */
FildesPack(uint16_t uHandleIdx,uint8_t bVolNum)2723     static int32_t FildesPack( uint16_t uHandleIdx,
2724                                uint8_t bVolNum )
2725     {
2726         int32_t iFildes;
2727 
2728         if( ( uHandleIdx >= REDCONF_HANDLE_COUNT ) || ( bVolNum >= REDCONF_VOLUME_COUNT ) )
2729         {
2730             REDERROR();
2731             iFildes = -1;
2732         }
2733         else
2734         {
2735             uint32_t ulFdBits;
2736 
2737             REDASSERT( gauGeneration[ bVolNum ] <= FD_GEN_MAX );
2738             REDASSERT( gauGeneration[ bVolNum ] != 0U );
2739 
2740             ulFdBits = gauGeneration[ bVolNum ];
2741             ulFdBits <<= FD_VOL_BITS;
2742             ulFdBits |= bVolNum;
2743             ulFdBits <<= FD_IDX_BITS;
2744             ulFdBits |= uHandleIdx;
2745 
2746             iFildes = ( int32_t ) ulFdBits;
2747 
2748             if( iFildes < FD_MIN )
2749             {
2750                 REDERROR();
2751                 iFildes = -1;
2752             }
2753         }
2754 
2755         return iFildes;
2756     }
2757 
2758 
2759 /** @brief Unpack a file descriptor.
2760  *
2761  *  @param iFildes      The file descriptor to unpack.
2762  *  @param puHandleIdx  If non-NULL, populated with the handle index extracted
2763  *                      from the file descriptor.
2764  *  @param pbVolNum     If non-NULL, populated with the volume number extracted
2765  *                      from the file descriptor.
2766  *  @param puGeneration If non-NULL, populated with the generation number
2767  *                      extracted from the file descriptor.
2768  */
FildesUnpack(int32_t iFildes,uint16_t * puHandleIdx,uint8_t * pbVolNum,uint16_t * puGeneration)2769     static void FildesUnpack( int32_t iFildes,
2770                               uint16_t * puHandleIdx,
2771                               uint8_t * pbVolNum,
2772                               uint16_t * puGeneration )
2773     {
2774         uint32_t ulFdBits = ( uint32_t ) iFildes;
2775 
2776         REDASSERT( iFildes >= FD_MIN );
2777 
2778         if( puHandleIdx != NULL )
2779         {
2780             *puHandleIdx = ( uint16_t ) ( ulFdBits & FD_IDX_MAX );
2781         }
2782 
2783         ulFdBits >>= FD_IDX_BITS;
2784 
2785         if( pbVolNum != NULL )
2786         {
2787             *pbVolNum = ( uint8_t ) ( ulFdBits & FD_VOL_MAX );
2788         }
2789 
2790         ulFdBits >>= FD_VOL_BITS;
2791 
2792         if( puGeneration != NULL )
2793         {
2794             *puGeneration = ( uint16_t ) ( ulFdBits & FD_GEN_MAX );
2795         }
2796     }
2797 
2798 
2799     #if REDCONF_API_POSIX_READDIR == 1
2800 
2801 /** @brief Validate a directory stream object.
2802  *
2803  *  @param pDirStream   The directory stream to validate.
2804  *
2805  *  @return Whether the directory stream is valid.
2806  *
2807  *  @retval true    The directory stream object appears valid.
2808  *  @retval false   The directory stream object is invalid.
2809  */
DirStreamIsValid(const REDDIR * pDirStream)2810         static bool DirStreamIsValid( const REDDIR * pDirStream )
2811         {
2812             bool fRet = true;
2813 
2814             if( pDirStream == NULL )
2815             {
2816                 fRet = false;
2817             }
2818             else
2819             {
2820                 uint16_t uHandleIdx;
2821 
2822                 /*  pDirStream should be a pointer to one of the handles.
2823                  *
2824                  *  A good compiler will optimize this loop into a bounds check and an
2825                  *  alignment check.
2826                  */
2827                 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ )
2828                 {
2829                     if( pDirStream == &gaHandle[ uHandleIdx ] )
2830                     {
2831                         break;
2832                     }
2833                 }
2834 
2835                 if( uHandleIdx < REDCONF_HANDLE_COUNT )
2836                 {
2837                     /*  The handle must be in use, have a valid volume number, and be a
2838                      *  directory handle.
2839                      */
2840                     if( ( pDirStream->ulInode == INODE_INVALID ) ||
2841                         ( pDirStream->bVolNum >= REDCONF_VOLUME_COUNT ) ||
2842                         ( ( pDirStream->bFlags & HFLAG_DIRECTORY ) == 0U ) )
2843                     {
2844                         fRet = false;
2845                     }
2846                 }
2847                 else
2848                 {
2849                     /*  pDirStream is a non-null pointer, but it is not a pointer to one
2850                      *  of our handles.
2851                      */
2852                     fRet = false;
2853                 }
2854             }
2855 
2856             return fRet;
2857         }
2858     #endif /* if REDCONF_API_POSIX_READDIR == 1 */
2859 
2860 
2861 /** @brief Enter the file system driver.
2862  *
2863  *  @return A negated ::REDSTATUS code indicating the operation result.
2864  *
2865  *  @retval 0           Operation was successful.
2866  *  @retval -RED_EINVAL The file system driver is uninitialized.
2867  *  @retval -RED_EUSERS Cannot become a file system user: too many users.
2868  */
PosixEnter(void)2869     static REDSTATUS PosixEnter( void )
2870     {
2871         REDSTATUS ret;
2872 
2873         if( gfPosixInited )
2874         {
2875             #if REDCONF_TASK_COUNT > 1U
2876                 RedOsMutexAcquire();
2877 
2878                 ret = TaskRegister( NULL );
2879 
2880                 if( ret != 0 )
2881                 {
2882                     RedOsMutexRelease();
2883                 }
2884             #else
2885                 ret = 0;
2886             #endif
2887         }
2888         else
2889         {
2890             ret = -RED_EINVAL;
2891         }
2892 
2893         return ret;
2894     }
2895 
2896 
2897 /** @brief Leave the file system driver.
2898  */
PosixLeave(void)2899     static void PosixLeave( void )
2900     {
2901         /*  If the driver was uninitialized, PosixEnter() should have failed and we
2902          *  should not be calling PosixLeave().
2903          */
2904         REDASSERT( gfPosixInited );
2905 
2906         #if REDCONF_TASK_COUNT > 1U
2907             RedOsMutexRelease();
2908         #endif
2909     }
2910 
2911 
2912 /** @brief Check that a mode is consistent with the given expected type.
2913  *
2914  *  @param uMode        An inode mode, indicating whether the inode is a file
2915  *                      or a directory.
2916  *  @param expectedType The expected type: ::FTYPE_FILE, ::FTYPE_DIR, or
2917  *                      ::FTYPE_EITHER.
2918  *
2919  *  @return A negated ::REDSTATUS code indicating the operation result.
2920  *
2921  *  @retval 0               Operation was successful.
2922  *  @retval -RED_EISDIR     Expected type is file, actual type is directory.
2923  *  @retval -RED_ENOTDIR    Expected type is directory, actual type is file.
2924  */
ModeTypeCheck(uint16_t uMode,FTYPE expectedType)2925     static REDSTATUS ModeTypeCheck( uint16_t uMode,
2926                                     FTYPE expectedType )
2927     {
2928         REDSTATUS ret;
2929 
2930         if( ( expectedType == FTYPE_FILE ) && RED_S_ISDIR( uMode ) )
2931         {
2932             /*  Expected file, found directory.
2933              */
2934             ret = -RED_EISDIR;
2935         }
2936         else if( ( expectedType == FTYPE_DIR ) && RED_S_ISREG( uMode ) )
2937         {
2938             /*  Expected directory, found file.
2939              */
2940             ret = -RED_ENOTDIR;
2941         }
2942         else
2943         {
2944             /*  No expected type or found what we expected.
2945              */
2946             ret = 0;
2947         }
2948 
2949         return ret;
2950     }
2951 
2952 
2953     #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) )
2954 
2955 /** @brief Check whether an inode can be unlinked.
2956  *
2957  *  If an inode has a link count of 1 (meaning unlinking another name would
2958  *  result in the deletion of the inode) and open handles, it cannot be deleted
2959  *  since this would break open handles.
2960  *
2961  *  @param ulInode  The inode whose name is to be unlinked.
2962  *
2963  *  @return A negated ::REDSTATUS code indicating the operation result.
2964  *
2965  *  @retval 0           Operation was successful.
2966  *  @retval -RED_EBADF  @p ulInode is not a valid inode.
2967  *  @retval -RED_EBUSY  The inode has a link count of one and open handles.
2968  *  @retval -RED_EIO    A disk I/O error occurred.
2969  */
InodeUnlinkCheck(uint32_t ulInode)2970         static REDSTATUS InodeUnlinkCheck( uint32_t ulInode )
2971         {
2972             uint16_t uHandleIdx;
2973             REDSTATUS ret;
2974 
2975             #if REDCONF_API_POSIX_LINK == 0
2976                 ret = 0;
2977             #else
2978                 REDSTAT InodeStat;
2979 
2980                 ret = RedCoreStat( ulInode, &InodeStat );
2981 
2982                 /*  We only need to check for open handles if the inode is down to its last
2983                  *  link.  If it has multiple links, the inode will continue to exist, so
2984                  *  deleting the name will not break the open handles.
2985                  */
2986                 if( ( ret == 0 ) && ( InodeStat.st_nlink == 1U ) )
2987             #endif
2988             {
2989                 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ )
2990                 {
2991                     if( ( gaHandle[ uHandleIdx ].ulInode == ulInode ) && ( gaHandle[ uHandleIdx ].bVolNum == gbRedVolNum ) )
2992                     {
2993                         ret = -RED_EBUSY;
2994                         break;
2995                     }
2996                 }
2997             }
2998 
2999             return ret;
3000         }
3001     #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) ) */
3002 
3003 
3004     #if REDCONF_TASK_COUNT > 1U
3005 
3006 /** @brief Register a task as a file system user, if it is not already
3007  *         registered as one.
3008  *
3009  *  The caller must hold the FS mutex.
3010  *
3011  *  @param pulTaskIdx   On successful return, if non-NULL, populated with the
3012  *                      index of the task slot assigned to the calling task.
3013  *                      This is populated whether or not the task had already
3014  *                      been registered.
3015  *
3016  *  @return A negated ::REDSTATUS code indicating the operation result.
3017  *
3018  *  @retval 0           Operation was successful.
3019  *  @retval -RED_EUSERS Cannot become a file system user: too many users.
3020  */
TaskRegister(uint32_t * pulTaskIdx)3021         static REDSTATUS TaskRegister( uint32_t * pulTaskIdx )
3022         {
3023             uint32_t ulTaskId = RedOsTaskId();
3024             uint32_t ulFirstFreeIdx = REDCONF_TASK_COUNT;
3025             uint32_t ulIdx;
3026             REDSTATUS ret;
3027 
3028             REDASSERT( ulTaskId != 0U );
3029 
3030             /*  Scan the task slots to determine if the task is registered as a file
3031              *  system task.
3032              */
3033             for( ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++ )
3034             {
3035                 if( gaTask[ ulIdx ].ulTaskId == ulTaskId )
3036                 {
3037                     break;
3038                 }
3039 
3040                 if( ( ulFirstFreeIdx == REDCONF_TASK_COUNT ) && ( gaTask[ ulIdx ].ulTaskId == 0U ) )
3041                 {
3042                     ulFirstFreeIdx = ulIdx;
3043                 }
3044             }
3045 
3046             if( ulIdx == REDCONF_TASK_COUNT )
3047             {
3048                 /*  Task not already registered.
3049                  */
3050                 if( ulFirstFreeIdx == REDCONF_TASK_COUNT )
3051                 {
3052                     /*  Cannot register task, no more slots.
3053                      */
3054                     ret = -RED_EUSERS;
3055                 }
3056                 else
3057                 {
3058                     /*  Registering task.
3059                      */
3060                     ulIdx = ulFirstFreeIdx;
3061                     gaTask[ ulIdx ].ulTaskId = ulTaskId;
3062                     ret = 0;
3063                 }
3064             }
3065             else
3066             {
3067                 /*  Task already registered.
3068                  */
3069                 ret = 0;
3070             }
3071 
3072             if( ( ret == 0 ) && ( pulTaskIdx != NULL ) )
3073             {
3074                 *pulTaskIdx = ulIdx;
3075             }
3076 
3077             return ret;
3078         }
3079     #endif /* REDCONF_TASK_COUNT > 1U */
3080 
3081 
3082 /** @brief Convert an error value into a simple 0 or -1 return.
3083  *
3084  *  This function is simple, but what it does is needed in many places.  It
3085  *  returns zero if @p iError is zero (meaning success) or it returns -1 if
3086  *  @p iError is nonzero (meaning error).  Also, if @p iError is nonzero, it
3087  *  is saved in red_errno.
3088  *
3089  *  @param  iError  The error value.
3090  *
3091  *  @return Returns 0 if @p iError is 0; otherwise, returns -1.
3092  */
PosixReturn(REDSTATUS iError)3093     static int32_t PosixReturn( REDSTATUS iError )
3094     {
3095         int32_t iReturn;
3096 
3097         if( iError == 0 )
3098         {
3099             iReturn = 0;
3100         }
3101         else
3102         {
3103             iReturn = -1;
3104 
3105             /*  The errors should be negative, and errno positive.
3106              */
3107             REDASSERT( iError < 0 );
3108             red_errno = -iError;
3109         }
3110 
3111         return iReturn;
3112     }
3113 
3114 
3115 #endif /* REDCONF_API_POSIX == 1 */
3116