1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
\r
3 Copyright (c) 2014-2015 Datalight, Inc.
\r
4 All Rights Reserved Worldwide.
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; use version 2 of the License.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
\r
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
19 /* Businesses and individuals that for commercial or other reasons cannot
\r
20 comply with the terms of the GPLv2 license may obtain a commercial license
\r
21 before incorporating Reliance Edge into proprietary software for
\r
22 distribution in any form. Visit http://www.datalight.com/reliance-edge for
\r
26 @brief Implementation of the the Reliance Edge POSIX-like API.
\r
31 #if REDCONF_API_POSIX == 1
\r
33 /** @defgroup red_group_posix The POSIX-like File System Interface
\r
37 #include <redvolume.h>
\r
38 #include <redcoreapi.h>
\r
39 #include <redposix.h>
\r
40 #include "redpath.h"
\r
43 /*-------------------------------------------------------------------
\r
45 -------------------------------------------------------------------*/
\r
47 #define FD_GEN_BITS 11U /* File descriptor bits for mount generation. */
\r
48 #define FD_VOL_BITS 8U /* File descriptor bits for volume number. */
\r
49 #define FD_IDX_BITS 12U /* File descriptor bits for handle index. */
\r
51 /* 31 bits available: file descriptors are int32_t, but the sign bit must
\r
54 #if (FD_GEN_BITS + FD_VOL_BITS + FD_IDX_BITS) > 31U
\r
55 #error "Internal error: too many file descriptor bits!"
\r
58 /* Maximum values for file descriptor components.
\r
60 #define FD_GEN_MAX ((1UL << FD_GEN_BITS) - 1U)
\r
61 #define FD_VOL_MAX ((1UL << FD_VOL_BITS) - 1U)
\r
62 #define FD_IDX_MAX ((1UL << FD_IDX_BITS) - 1U)
\r
64 #if REDCONF_VOLUME_COUNT > FD_VOL_MAX
\r
65 #error "Error: Too many file system volumes!"
\r
67 #if REDCONF_HANDLE_COUNT > (FD_IDX_MAX + 1U)
\r
68 #error "Error: Too many file system handles!"
\r
71 /* File descriptors must never be negative; and must never be zero, one, or
\r
72 two, to avoid confusion with STDIN, STDOUT, and STDERR.
\r
76 /*-------------------------------------------------------------------
\r
78 -------------------------------------------------------------------*/
\r
80 /* Mask of all RED_O_* values.
\r
82 #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)
\r
84 #define HFLAG_DIRECTORY 0x01U /* Handle is for a directory. */
\r
85 #define HFLAG_READABLE 0x02U /* Handle is readable. */
\r
86 #define HFLAG_WRITEABLE 0x04U /* Handle is writeable. */
\r
87 #define HFLAG_APPENDING 0x08U /* Handle was opened in append mode. */
\r
89 /* @brief Handle structure, used to implement file descriptors and directory
\r
92 typedef struct sREDHANDLE
\r
94 uint32_t ulInode; /**< Inode number; 0 if handle is available. */
\r
95 uint8_t bVolNum; /**< Volume containing the inode. */
\r
96 uint8_t bFlags; /**< Handle flags (type and mode). */
\r
97 uint64_t ullOffset; /**< File or directory offset. */
\r
98 #if REDCONF_API_POSIX_READDIR == 1
\r
99 REDDIRENT dirent; /**< Dirent structure returned by red_readdir(). */
\r
103 /*-------------------------------------------------------------------
\r
105 -------------------------------------------------------------------*/
\r
107 #if REDCONF_TASK_COUNT > 1U
\r
108 /* @brief Per-task information.
\r
112 uint32_t ulTaskId; /**< ID of the task which owns this slot; 0 if free. */
\r
113 REDSTATUS iErrno; /**< Last error value. */
\r
117 /*-------------------------------------------------------------------
\r
119 -------------------------------------------------------------------*/
\r
121 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1))
\r
122 static REDSTATUS UnlinkSub(const char *pszPath, FTYPE type);
\r
124 static REDSTATUS FildesOpen(const char *pszPath, uint32_t ulOpenMode, FTYPE type, int32_t *piFildes);
\r
125 static REDSTATUS FildesClose(int32_t iFildes);
\r
126 static REDSTATUS FildesToHandle(int32_t iFildes, FTYPE expectedType, REDHANDLE **ppHandle);
\r
127 static int32_t FildesPack(uint16_t uHandleIdx, uint8_t bVolNum);
\r
128 static void FildesUnpack(int32_t iFildes, uint16_t *puHandleIdx, uint8_t *pbVolNum, uint16_t *puGeneration);
\r
129 #if REDCONF_API_POSIX_READDIR == 1
\r
130 static bool DirStreamIsValid(const REDDIR *pDirStream);
\r
132 static REDSTATUS PosixEnter(void);
\r
133 static void PosixLeave(void);
\r
134 static REDSTATUS ModeTypeCheck(uint16_t uMode, FTYPE expectedType);
\r
135 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) || ((REDCONF_API_POSIX_RENAME == 1) && (REDCONF_RENAME_ATOMIC == 1)))
\r
136 static REDSTATUS InodeUnlinkCheck(uint32_t ulInode);
\r
138 #if REDCONF_TASK_COUNT > 1U
\r
139 static REDSTATUS TaskRegister(uint32_t *pulTaskIdx);
\r
141 static int32_t PosixReturn(REDSTATUS iError);
\r
143 /*-------------------------------------------------------------------
\r
145 -------------------------------------------------------------------*/
\r
147 static bool gfPosixInited; /* Whether driver is initialized. */
\r
148 static REDHANDLE gaHandle[REDCONF_HANDLE_COUNT]; /* Array of all handles. */
\r
149 #if REDCONF_TASK_COUNT > 1U
\r
150 static TASKSLOT gaTask[REDCONF_TASK_COUNT]; /* Array of task slots. */
\r
153 /* Array of volume mount "generations". These are incremented for a volume
\r
154 each time that volume is mounted. The generation number (along with the
\r
155 volume number) is incorporated into the file descriptors; a stale file
\r
156 descriptor from a previous mount can be detected since it will include a
\r
157 stale generation number.
\r
159 static uint16_t gauGeneration[REDCONF_VOLUME_COUNT];
\r
162 /*-------------------------------------------------------------------
\r
164 -------------------------------------------------------------------*/
\r
166 /** @brief Initialize the Reliance Edge file system driver.
\r
168 Prepares the Reliance Edge file system driver to be used. Must be the first
\r
169 Reliance Edge function to be invoked: no volumes can be mounted or formatted
\r
170 until the driver has been initialized.
\r
172 If this function is called when the Reliance Edge driver is already
\r
173 initialized, it does nothing and returns success.
\r
175 This function is not thread safe: attempting to initialize from multiple
\r
176 threads could leave things in a bad state.
\r
178 @return On success, zero is returned. On error, -1 is returned and
\r
179 #red_errno is set appropriately.
\r
181 <b>Errno values</b>
\r
182 - #RED_EINVAL: The volume path prefix configuration is invalid.
\r
184 int32_t red_init(void)
\r
194 ret = RedCoreInit();
\r
197 RedMemSet(gaHandle, 0U, sizeof(gaHandle));
\r
199 #if REDCONF_TASK_COUNT > 1U
\r
200 RedMemSet(gaTask, 0U, sizeof(gaTask));
\r
203 gfPosixInited = true;
\r
207 return PosixReturn(ret);
\r
211 /** @brief Uninitialize the Reliance Edge file system driver.
\r
213 Tears down the Reliance Edge file system driver. Cannot be used until all
\r
214 Reliance Edge volumes are unmounted. A subsequent call to red_init() will
\r
215 initialize the driver again.
\r
217 If this function is called when the Reliance Edge driver is already
\r
218 uninitialized, it does nothing and returns success.
\r
220 This function is not thread safe: attempting to uninitialize from multiple
\r
221 threads could leave things in a bad state.
\r
223 @return On success, zero is returned. On error, -1 is returned and
\r
224 #red_errno is set appropriately.
\r
226 <b>Errno values</b>
\r
227 - #RED_EBUSY: At least one volume is still mounted.
\r
229 int32_t red_uninit(void)
\r
235 ret = PosixEnter();
\r
241 for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)
\r
243 if(gaRedVolume[bVolNum].fMounted)
\r
252 /* All volumes are unmounted. Mark the driver as
\r
253 uninitialized before releasing the FS mutex, to avoid any
\r
254 race condition where a volume could be mounted and then the
\r
255 driver uninitialized with a mounted volume.
\r
257 gfPosixInited = false;
\r
260 /* The FS mutex must be released before we uninitialize the core,
\r
261 since the FS mutex needs to be in the released state when it
\r
262 gets uninitialized.
\r
264 Don't use PosixLeave(), since it asserts gfPosixInited is true.
\r
266 #if REDCONF_TASK_COUNT > 1U
\r
267 RedOsMutexRelease();
\r
273 ret = RedCoreUninit();
\r
275 /* Not good if the above fails, since things might be partly, but
\r
276 not entirely, torn down, and there might not be a way back to
\r
277 a valid driver state.
\r
279 REDASSERT(ret == 0);
\r
287 return PosixReturn(ret);
\r
291 /** @brief Mount a file system volume.
\r
293 Prepares the file system volume to be accessed. Mount will fail if the
\r
294 volume has never been formatted, or if the on-disk format is inconsistent
\r
295 with the compile-time configuration.
\r
297 An error is returned if the volume is already mounted.
\r
299 @param pszVolume A path prefix identifying the volume to mount.
\r
301 @return On success, zero is returned. On error, -1 is returned and
\r
302 #red_errno is set appropriately.
\r
304 <b>Errno values</b>
\r
305 - #RED_EBUSY: Volume is already mounted.
\r
306 - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized.
\r
307 - #RED_EIO: Volume not formatted, improperly formatted, or corrupt.
\r
308 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
309 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
312 const char *pszVolume)
\r
316 ret = PosixEnter();
\r
322 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
324 /* The core will return success if the volume is already mounted, so
\r
325 check for that condition here to propagate the error.
\r
327 if((ret == 0) && gaRedVolume[bVolNum].fMounted)
\r
332 #if REDCONF_VOLUME_COUNT > 1U
\r
335 ret = RedCoreVolSetCurrent(bVolNum);
\r
341 ret = RedCoreVolMount();
\r
346 /* Increment the mount generation, invalidating file descriptors
\r
347 from previous mounts. Note that while the generation numbers
\r
348 are stored in 16-bit values, we have less than 16-bits to store
\r
349 generations in the file descriptors, so we must wrap-around
\r
352 gauGeneration[bVolNum]++;
\r
353 if(gauGeneration[bVolNum] > FD_GEN_MAX)
\r
355 /* Wrap-around to one, rather than zero. The generation is
\r
356 stored in the top bits of the file descriptor, and doing
\r
357 this means that low numbers are never valid file
\r
358 descriptors. This implements the requirement that 0, 1,
\r
359 and 2 are never valid file descriptors, thereby avoiding
\r
360 confusion with STDIN, STDOUT, and STDERR.
\r
362 gauGeneration[bVolNum] = 1U;
\r
369 return PosixReturn(ret);
\r
373 /** @brief Unmount a file system volume.
\r
375 This function discards the in-memory state for the file system and marks it
\r
376 as unmounted. Subsequent attempts to access the volume will fail until the
\r
377 volume is mounted again.
\r
379 If unmount automatic transaction points are enabled, this function will
\r
380 commit a transaction point prior to unmounting. If unmount automatic
\r
381 transaction points are disabled, this function will unmount without
\r
382 transacting, effectively discarding the working state.
\r
384 Before unmounting, this function will wait for any active file system
\r
385 thread to complete by acquiring the FS mutex. The volume will be marked as
\r
386 unmounted before the FS mutex is released, so subsequent FS threads will
\r
387 possibly block and then see an error when attempting to access a volume
\r
388 which is unmounting or unmounted. If the volume has open handles, the
\r
391 An error is returned if the volume is already unmounted.
\r
393 @param pszVolume A path prefix identifying the volume to unmount.
\r
395 @return On success, zero is returned. On error, -1 is returned and
\r
396 #red_errno is set appropriately.
\r
398 <b>Errno values</b>
\r
399 - #RED_EBUSY: There are still open handles for this file system volume.
\r
400 - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized; or
\r
401 the volume is already unmounted.
\r
402 - #RED_EIO: I/O error during unmount automatic transaction point.
\r
403 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
404 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
406 int32_t red_umount(
\r
407 const char *pszVolume)
\r
411 ret = PosixEnter();
\r
416 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
418 /* The core will return success if the volume is already unmounted, so
\r
419 check for that condition here to propagate the error.
\r
421 if((ret == 0) && !gaRedVolume[bVolNum].fMounted)
\r
428 uint16_t uHandleIdx;
\r
430 /* Do not unmount the volume if it still has open handles.
\r
432 for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)
\r
434 const REDHANDLE *pHandle = &gaHandle[uHandleIdx];
\r
436 if((pHandle->ulInode != INODE_INVALID) && (pHandle->bVolNum == bVolNum))
\r
444 #if REDCONF_VOLUME_COUNT > 1U
\r
447 ret = RedCoreVolSetCurrent(bVolNum);
\r
453 ret = RedCoreVolUnmount();
\r
459 return PosixReturn(ret);
\r
463 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1)
\r
464 /** @brief Format a file system volume.
\r
466 Uses the statically defined volume configuration. After calling this
\r
467 function, the volume needs to be mounted -- see red_mount().
\r
469 An error is returned if the volume is mounted.
\r
471 @param pszVolume A path prefix identifying the volume to format.
\r
473 @return On success, zero is returned. On error, -1 is returned and
\r
474 #red_errno is set appropriately.
\r
476 <b>Errno values</b>
\r
477 - #RED_EBUSY: Volume is mounted.
\r
478 - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized.
\r
479 - #RED_EIO: I/O error formatting the volume.
\r
480 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
481 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
483 int32_t red_format(
\r
484 const char *pszVolume)
\r
488 ret = PosixEnter();
\r
493 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
495 #if REDCONF_VOLUME_COUNT > 1U
\r
498 ret = RedCoreVolSetCurrent(bVolNum);
\r
504 ret = RedCoreVolFormat();
\r
510 return PosixReturn(ret);
\r
515 #if REDCONF_READ_ONLY == 0
\r
516 /** @brief Commit a transaction point.
\r
518 Reliance Edge is a transactional file system. All modifications, of both
\r
519 metadata and filedata, are initially working state. A transaction point
\r
520 is a process whereby the working state atomically becomes the committed
\r
521 state, replacing the previous committed state. Whenever Reliance Edge is
\r
522 mounted, including after power loss, the state of the file system after
\r
523 mount is the most recent committed state. Nothing from the committed
\r
524 state is ever missing, and nothing from the working state is ever included.
\r
526 @param pszVolume A path prefix identifying the volume to transact.
\r
528 @return On success, zero is returned. On error, -1 is returned and
\r
529 #red_errno is set appropriately.
\r
531 <b>Errno values</b>
\r
532 - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`.
\r
533 - #RED_EIO: I/O error during the transaction point.
\r
534 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
535 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
537 int32_t red_transact(
\r
538 const char *pszVolume)
\r
542 ret = PosixEnter();
\r
547 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
549 #if REDCONF_VOLUME_COUNT > 1U
\r
552 ret = RedCoreVolSetCurrent(bVolNum);
\r
558 ret = RedCoreVolTransact();
\r
564 return PosixReturn(ret);
\r
569 #if REDCONF_READ_ONLY == 0
\r
570 /** @brief Update the transaction mask.
\r
572 The following events are available:
\r
574 - #RED_TRANSACT_UMOUNT
\r
575 - #RED_TRANSACT_CREAT
\r
576 - #RED_TRANSACT_UNLINK
\r
577 - #RED_TRANSACT_MKDIR
\r
578 - #RED_TRANSACT_RENAME
\r
579 - #RED_TRANSACT_LINK
\r
580 - #RED_TRANSACT_CLOSE
\r
581 - #RED_TRANSACT_WRITE
\r
582 - #RED_TRANSACT_FSYNC
\r
583 - #RED_TRANSACT_TRUNCATE
\r
584 - #RED_TRANSACT_VOLFULL
\r
586 The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all
\r
587 automatic transaction events. The #RED_TRANSACT_MASK macro is a bitmask
\r
588 of all transaction flags, excluding those representing excluded
\r
591 Attempting to enable events for excluded functionality will result in an
\r
594 @param pszVolume The path prefix of the volume whose transaction mask is
\r
596 @param ulEventMask A bitwise-OR'd mask of automatic transaction events to
\r
597 be set as the current transaction mode.
\r
599 @return On success, zero is returned. On error, -1 is returned and
\r
600 #red_errno is set appropriately.
\r
602 <b>Errno values</b>
\r
603 - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
\r
604 @p ulEventMask contains invalid bits.
\r
605 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
606 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
608 int32_t red_settransmask(
\r
609 const char *pszVolume,
\r
610 uint32_t ulEventMask)
\r
614 ret = PosixEnter();
\r
619 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
621 #if REDCONF_VOLUME_COUNT > 1U
\r
624 ret = RedCoreVolSetCurrent(bVolNum);
\r
630 ret = RedCoreTransMaskSet(ulEventMask);
\r
636 return PosixReturn(ret);
\r
641 /** @brief Read the transaction mask.
\r
643 If the volume is read-only, the returned event mask is always zero.
\r
645 @param pszVolume The path prefix of the volume whose transaction mask is
\r
647 @param pulEventMask Populated with a bitwise-OR'd mask of automatic
\r
648 transaction events which represent the current
\r
649 transaction mode for the volume.
\r
651 @return On success, zero is returned. On error, -1 is returned and
\r
652 #red_errno is set appropriately.
\r
654 <b>Errno values</b>
\r
655 - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
\r
656 @p pulEventMask is `NULL`.
\r
657 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
658 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
660 int32_t red_gettransmask(
\r
661 const char *pszVolume,
\r
662 uint32_t *pulEventMask)
\r
666 ret = PosixEnter();
\r
671 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
673 #if REDCONF_VOLUME_COUNT > 1U
\r
676 ret = RedCoreVolSetCurrent(bVolNum);
\r
682 ret = RedCoreTransMaskGet(pulEventMask);
\r
688 return PosixReturn(ret);
\r
692 /** @brief Query file system status information.
\r
694 @p pszVolume should name a valid volume prefix or a valid root directory;
\r
695 this differs from POSIX statvfs, where any existing file or directory is a
\r
698 @param pszVolume The path prefix of the volume to query.
\r
699 @param pStatvfs The buffer to populate with volume information.
\r
701 @return On success, zero is returned. On error, -1 is returned and
\r
702 #red_errno is set appropriately.
\r
704 <b>Errno values</b>
\r
705 - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or
\r
706 @p pStatvfs is `NULL`.
\r
707 - #RED_ENOENT: @p pszVolume is not a valid volume path prefix.
\r
708 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
710 int32_t red_statvfs(
\r
711 const char *pszVolume,
\r
712 REDSTATFS *pStatvfs)
\r
716 ret = PosixEnter();
\r
721 ret = RedPathSplit(pszVolume, &bVolNum, NULL);
\r
723 #if REDCONF_VOLUME_COUNT > 1U
\r
726 ret = RedCoreVolSetCurrent(bVolNum);
\r
732 ret = RedCoreVolStat(pStatvfs);
\r
738 return PosixReturn(ret);
\r
742 /** @brief Open a file or directory.
\r
744 Exactly one file access mode must be specified:
\r
746 - #RED_O_RDONLY: Open for reading only.
\r
747 - #RED_O_WRONLY: Open for writing only.
\r
748 - #RED_O_RDWR: Open for reading and writing.
\r
750 Directories can only be opened with `RED_O_RDONLY`.
\r
752 The following flags may also be used:
\r
754 - #RED_O_APPEND: Set the file offset to the end-of-file prior to each
\r
756 - #RED_O_CREAT: Create the named file if it does not exist.
\r
757 - #RED_O_EXCL: In combination with `RED_O_CREAT`, return an error if the
\r
758 path already exists.
\r
759 - #RED_O_TRUNC: Truncate the opened file to size zero. Only supported when
\r
760 #REDCONF_API_POSIX_FTRUNCATE is true.
\r
762 #RED_O_CREAT, #RED_O_EXCL, and #RED_O_TRUNC are invalid with #RED_O_RDONLY.
\r
763 #RED_O_EXCL is invalid without #RED_O_CREAT.
\r
765 If the volume is read-only, #RED_O_RDONLY is the only valid open flag; use
\r
766 of any other flag will result in an error.
\r
768 If #RED_O_TRUNC frees data which is in the committed state, it will not
\r
769 return to free space until after a transaction point.
\r
771 The returned file descriptor must later be closed with red_close().
\r
773 Unlike POSIX open, there is no optional third argument for the permissions
\r
774 (which Reliance Edge does not use) and other open flags (like `O_SYNC`) are
\r
777 @param pszPath The path to the file or directory.
\r
778 @param ulOpenMode The open flags (mask of `RED_O_` values).
\r
780 @return On success, a nonnegative file descriptor is returned. On error,
\r
781 -1 is returned and #red_errno is set appropriately.
\r
783 <b>Errno values</b>
\r
784 - #RED_EEXIST: Using #RED_O_CREAT and #RED_O_EXCL, and the indicated path
\r
786 - #RED_EINVAL: @p ulOpenMode is invalid; or @p pszPath is `NULL`; or the
\r
787 volume containing the path is not mounted.
\r
788 - #RED_EIO: A disk I/O error occurred.
\r
789 - #RED_EISDIR: The path names a directory and @p ulOpenMode includes
\r
790 #RED_O_WRONLY or #RED_O_RDWR.
\r
791 - #RED_EMFILE: There are no available file descriptors.
\r
792 - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
\r
794 - #RED_ENFILE: Attempting to create a file but the file system has used all
\r
795 available inode slots.
\r
796 - #RED_ENOENT: #RED_O_CREAT is not set and the named file does not exist; or
\r
797 #RED_O_CREAT is set and the parent directory does not exist; or the
\r
798 volume does not exist; or the @p pszPath argument, after removing the
\r
799 volume prefix, points to an empty string.
\r
800 - #RED_ENOSPC: The file does not exist and #RED_O_CREAT was specified, but
\r
801 there is insufficient free space to expand the directory or to create the
\r
803 - #RED_ENOTDIR: A component of the prefix in @p pszPath does not name a
\r
805 - #RED_EROFS: The path resides on a read-only file system and a write
\r
806 operation was requested.
\r
807 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
810 const char *pszPath,
\r
811 uint32_t ulOpenMode)
\r
813 int32_t iFildes = -1; /* Init'd to quiet warnings. */
\r
816 #if REDCONF_READ_ONLY == 1
\r
817 if(ulOpenMode != RED_O_RDONLY)
\r
822 if( (ulOpenMode != (ulOpenMode & RED_O_MASK))
\r
823 || ((ulOpenMode & (RED_O_RDONLY|RED_O_WRONLY|RED_O_RDWR)) == 0U)
\r
824 || (((ulOpenMode & RED_O_RDONLY) != 0U) && ((ulOpenMode & (RED_O_WRONLY|RED_O_RDWR)) != 0U))
\r
825 || (((ulOpenMode & RED_O_WRONLY) != 0U) && ((ulOpenMode & (RED_O_RDONLY|RED_O_RDWR)) != 0U))
\r
826 || (((ulOpenMode & RED_O_RDWR) != 0U) && ((ulOpenMode & (RED_O_RDONLY|RED_O_WRONLY)) != 0U))
\r
827 || (((ulOpenMode & (RED_O_TRUNC|RED_O_CREAT|RED_O_EXCL)) != 0U) && ((ulOpenMode & RED_O_RDONLY) != 0U))
\r
828 || (((ulOpenMode & RED_O_EXCL) != 0U) && ((ulOpenMode & RED_O_CREAT) == 0U)))
\r
832 #if REDCONF_API_POSIX_FTRUNCATE == 0
\r
833 else if((ulOpenMode & RED_O_TRUNC) != 0U)
\r
841 ret = PosixEnter();
\r
846 ret = FildesOpen(pszPath, ulOpenMode, FTYPE_EITHER, &iFildes);
\r
853 iFildes = PosixReturn(ret);
\r
860 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_UNLINK == 1)
\r
861 /** @brief Delete a file or directory.
\r
863 The given name is deleted and the link count of the corresponding inode is
\r
864 decremented. If the link count falls to zero (no remaining hard links),
\r
865 the inode will be deleted.
\r
867 Unlike POSIX unlink, deleting a file or directory with open handles (file
\r
868 descriptors or directory streams) will fail with an #RED_EBUSY error. This
\r
869 only applies when deleting an inode with a link count of one; if a file has
\r
870 multiple names (hard links), all but the last name may be deleted even if
\r
873 If the path names a directory which is not empty, the unlink will fail.
\r
875 If the deletion frees data in the committed state, it will not return to
\r
876 free space until after a transaction point.
\r
878 Unlike POSIX unlink, this function can fail when the disk is full. To fix
\r
879 this, transact and try again: Reliance Edge guarantees that it is possible
\r
880 to delete at least one file or directory after a transaction point. If disk
\r
881 full automatic transactions are enabled, this will happen automatically.
\r
883 @param pszPath The path of the file or directory to delete.
\r
885 @return On success, zero is returned. On error, -1 is returned and
\r
886 #red_errno is set appropriately.
\r
888 <b>Errno values</b>
\r
889 - #RED_EBUSY: @p pszPath points to an inode with open handles and a link
\r
891 - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
\r
893 - #RED_EIO: A disk I/O error occurred.
\r
894 - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
\r
896 - #RED_ENOENT: The path does not name an existing file; or the @p pszPath
\r
897 argument, after removing the volume prefix, points to an empty string.
\r
898 - #RED_ENOTDIR: A component of the path prefix is not a directory.
\r
899 - #RED_ENOTEMPTY: The path names a directory which is not empty.
\r
900 - #RED_ENOSPC: The file system does not have enough space to modify the
\r
901 parent directory to perform the deletion.
\r
902 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
904 int32_t red_unlink(
\r
905 const char *pszPath)
\r
909 ret = PosixEnter();
\r
912 ret = UnlinkSub(pszPath, FTYPE_EITHER);
\r
917 return PosixReturn(ret);
\r
922 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_MKDIR == 1)
\r
923 /** @brief Create a new directory.
\r
925 Unlike POSIX mkdir, this function has no second argument for the
\r
926 permissions (which Reliance Edge does not use).
\r
928 @param pszPath The name and location of the directory to create.
\r
930 @return On success, zero is returned. On error, -1 is returned and
\r
931 #red_errno is set appropriately.
\r
933 <b>Errno values</b>
\r
934 - #RED_EEXIST: @p pszPath points to an existing file or directory.
\r
935 - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
\r
937 - #RED_EIO: A disk I/O error occurred.
\r
938 - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
\r
940 - #RED_ENOENT: A component of the path prefix does not name an existing
\r
941 directory; or the @p pszPath argument, after removing the volume prefix,
\r
942 points to an empty string.
\r
943 - #RED_ENOSPC: The file system does not have enough space for the new
\r
944 directory or to extend the parent directory of the new directory.
\r
945 - #RED_ENOTDIR: A component of the path prefix is not a directory.
\r
946 - #RED_EROFS: The parent directory resides on a read-only file system.
\r
947 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
950 const char *pszPath)
\r
954 ret = PosixEnter();
\r
957 const char *pszLocalPath;
\r
960 ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);
\r
962 #if REDCONF_VOLUME_COUNT > 1U
\r
965 ret = RedCoreVolSetCurrent(bVolNum);
\r
971 const char *pszName;
\r
974 ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);
\r
980 ret = RedCoreCreate(ulPInode, pszName, true, &ulInode);
\r
987 return PosixReturn(ret);
\r
992 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RMDIR == 1)
\r
993 /** @brief Delete a directory.
\r
995 The given directory name is deleted and the corresponding directory inode
\r
998 Unlike POSIX rmdir, deleting a directory with open handles (file
\r
999 descriptors or directory streams) will fail with an #RED_EBUSY error.
\r
1001 If the path names a directory which is not empty, the deletion will fail.
\r
1002 If the path names the root directory of a file system volume, the deletion
\r
1005 If the path names a regular file, the deletion will fail. This provides
\r
1006 type checking and may be useful in cases where an application knows the
\r
1007 path to be deleted should name a directory.
\r
1009 If the deletion frees data in the committed state, it will not return to
\r
1010 free space until after a transaction point.
\r
1012 Unlike POSIX rmdir, this function can fail when the disk is full. To fix
\r
1013 this, transact and try again: Reliance Edge guarantees that it is possible
\r
1014 to delete at least one file or directory after a transaction point. If disk
\r
1015 full automatic transactions are enabled, this will happen automatically.
\r
1017 @param pszPath The path of the directory to delete.
\r
1019 @return On success, zero is returned. On error, -1 is returned and
\r
1020 #red_errno is set appropriately.
\r
1022 <b>Errno values</b>
\r
1023 - #RED_EBUSY: @p pszPath points to a directory with open handles.
\r
1024 - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
\r
1026 - #RED_EIO: A disk I/O error occurred.
\r
1027 - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than
\r
1028 #REDCONF_NAME_MAX.
\r
1029 - #RED_ENOENT: The path does not name an existing directory; or the
\r
1030 @p pszPath argument, after removing the volume prefix, points to an empty
\r
1032 - #RED_ENOTDIR: A component of the path is not a directory.
\r
1033 - #RED_ENOTEMPTY: The path names a directory which is not empty.
\r
1034 - #RED_ENOSPC: The file system does not have enough space to modify the
\r
1035 parent directory to perform the deletion.
\r
1036 - #RED_EROFS: The directory to be removed resides on a read-only file
\r
1038 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1040 int32_t red_rmdir(
\r
1041 const char *pszPath)
\r
1045 ret = PosixEnter();
\r
1048 ret = UnlinkSub(pszPath, FTYPE_DIR);
\r
1053 return PosixReturn(ret);
\r
1058 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RENAME == 1)
\r
1059 /** @brief Rename a file or directory.
\r
1061 Both paths must reside on the same file system volume. Attempting to use
\r
1062 this API to move a file to a different volume will result in an error.
\r
1064 If @p pszNewPath names an existing file or directory, the behavior depends
\r
1065 on the configuration. If #REDCONF_RENAME_ATOMIC is false, and if the
\r
1066 destination name exists, this function always fails and sets #red_errno to
\r
1067 #RED_EEXIST. This behavior is contrary to POSIX.
\r
1069 If #REDCONF_RENAME_ATOMIC is true, and if the new name exists, then in one
\r
1070 atomic operation, @p pszNewPath is unlinked and @p pszOldPath is renamed to
\r
1071 @p pszNewPath. Both @p pszNewPath and @p pszOldPath must be of the same
\r
1072 type (both files or both directories). As with red_unlink(), if
\r
1073 @p pszNewPath is a directory, it must be empty. The major exception to this
\r
1074 behavior is that if both @p pszOldPath and @p pszNewPath are links to the
\r
1075 same inode, then the rename does nothing and both names continue to exist.
\r
1076 Unlike POSIX rename, if @p pszNewPath points to an inode with a link count
\r
1077 of one and open handles (file descriptors or directory streams), the
\r
1078 rename will fail with #RED_EBUSY.
\r
1080 If the rename deletes the old destination, it may free data in the
\r
1081 committed state, which will not return to free space until after a
\r
1082 transaction point. Similarly, if the deleted inode was part of the
\r
1083 committed state, the inode slot will not be available until after a
\r
1084 transaction point.
\r
1086 @param pszOldPath The path of the file or directory to rename.
\r
1087 @param pszNewPath The new name and location after the rename.
\r
1089 @return On success, zero is returned. On error, -1 is returned and
\r
1090 #red_errno is set appropriately.
\r
1092 <b>Errno values</b>
\r
1093 - #RED_EBUSY: #REDCONF_RENAME_ATOMIC is true and @p pszNewPath points to an
\r
1094 inode with open handles and a link count of one.
\r
1095 - #RED_EEXIST: #REDCONF_RENAME_ATOMIC is false and @p pszNewPath exists.
\r
1096 - #RED_EINVAL: @p pszOldPath is `NULL`; or @p pszNewPath is `NULL`; or the
\r
1097 volume containing the path is not mounted.
\r
1098 - #RED_EIO: A disk I/O error occurred.
\r
1099 - #RED_EISDIR: The @p pszNewPath argument names a directory and the
\r
1100 @p pszOldPath argument names a non-directory.
\r
1101 - #RED_ENAMETOOLONG: The length of a component of either @p pszOldPath or
\r
1102 @p pszNewPath is longer than #REDCONF_NAME_MAX.
\r
1103 - #RED_ENOENT: The link named by @p pszOldPath does not name an existing
\r
1104 entry; or either @p pszOldPath or @p pszNewPath, after removing the volume
\r
1105 prefix, point to an empty string.
\r
1106 - #RED_ENOTDIR: A component of either path prefix is not a directory; or
\r
1107 @p pszOldPath names a directory and @p pszNewPath names a file.
\r
1108 - #RED_ENOTEMPTY: The path named by @p pszNewPath is a directory which is
\r
1110 - #RED_ENOSPC: The file system does not have enough space to extend the
\r
1111 directory that would contain @p pszNewPath.
\r
1112 - #RED_EROFS: The directory to be removed resides on a read-only file
\r
1114 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1115 - #RED_EXDEV: @p pszOldPath and @p pszNewPath are on different file system
\r
1118 int32_t red_rename(
\r
1119 const char *pszOldPath,
\r
1120 const char *pszNewPath)
\r
1124 ret = PosixEnter();
\r
1127 const char *pszOldLocalPath;
\r
1128 uint8_t bOldVolNum;
\r
1130 ret = RedPathSplit(pszOldPath, &bOldVolNum, &pszOldLocalPath);
\r
1134 const char *pszNewLocalPath;
\r
1135 uint8_t bNewVolNum;
\r
1137 ret = RedPathSplit(pszNewPath, &bNewVolNum, &pszNewLocalPath);
\r
1139 if((ret == 0) && (bOldVolNum != bNewVolNum))
\r
1144 #if REDCONF_VOLUME_COUNT > 1U
\r
1147 ret = RedCoreVolSetCurrent(bOldVolNum);
\r
1153 const char *pszOldName;
\r
1154 uint32_t ulOldPInode;
\r
1156 ret = RedPathToName(pszOldLocalPath, &ulOldPInode, &pszOldName);
\r
1160 const char *pszNewName;
\r
1161 uint32_t ulNewPInode;
\r
1163 ret = RedPathToName(pszNewLocalPath, &ulNewPInode, &pszNewName);
\r
1165 #if REDCONF_RENAME_ATOMIC == 1
\r
1168 uint32_t ulDestInode;
\r
1170 ret = RedCoreLookup(ulNewPInode, pszNewName, &ulDestInode);
\r
1173 ret = InodeUnlinkCheck(ulDestInode);
\r
1175 else if(ret == -RED_ENOENT)
\r
1181 /* Unexpected error, nothing to do.
\r
1189 ret = RedCoreRename(ulOldPInode, pszOldName, ulNewPInode, pszNewName);
\r
1198 return PosixReturn(ret);
\r
1203 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_LINK == 1)
\r
1204 /** @brief Create a hard link.
\r
1206 This creates an additional name (link) for the file named by @p pszPath.
\r
1207 The new name refers to the same file with the same contents. If a name is
\r
1208 deleted, but the underlying file has other names, the file continues to
\r
1209 exist. The link count (accessible via red_fstat()) indicates the number of
\r
1210 names that a file has. All of a file's names are on equal footing: there
\r
1211 is nothing special about the original name.
\r
1213 If @p pszPath names a directory, the operation will fail.
\r
1215 @param pszPath The path indicating the inode for the new link.
\r
1216 @param pszHardLink The name and location for the new link.
\r
1218 @return On success, zero is returned. On error, -1 is returned and
\r
1219 #red_errno is set appropriately.
\r
1221 <b>Errno values</b>
\r
1222 - #RED_EEXIST: @p pszHardLink resolves to an existing file.
\r
1223 - #RED_EINVAL: @p pszPath or @p pszHardLink is `NULL`; or the volume
\r
1224 containing the paths is not mounted.
\r
1225 - #RED_EIO: A disk I/O error occurred.
\r
1226 - #RED_EMLINK: Creating the link would exceed the maximum link count of the
\r
1227 inode named by @p pszPath.
\r
1228 - #RED_ENAMETOOLONG: The length of a component of either @p pszPath or
\r
1229 @p pszHardLink is longer than #REDCONF_NAME_MAX.
\r
1230 - #RED_ENOENT: A component of either path prefix does not exist; or the file
\r
1231 named by @p pszPath does not exist; or either @p pszPath or
\r
1232 @p pszHardLink, after removing the volume prefix, point to an empty
\r
1234 - #RED_ENOSPC: There is insufficient free space to expand the directory that
\r
1235 would contain the link.
\r
1236 - #RED_ENOTDIR: A component of either path prefix is not a directory.
\r
1237 - #RED_EPERM: The @p pszPath argument names a directory.
\r
1238 - #RED_EROFS: The requested link requires writing in a directory on a
\r
1239 read-only file system.
\r
1240 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1241 - #RED_EXDEV: @p pszPath and @p pszHardLink are on different file system
\r
1245 const char *pszPath,
\r
1246 const char *pszHardLink)
\r
1250 ret = PosixEnter();
\r
1253 const char *pszLocalPath;
\r
1256 ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);
\r
1260 const char *pszLinkLocalPath;
\r
1261 uint8_t bLinkVolNum;
\r
1263 ret = RedPathSplit(pszHardLink, &bLinkVolNum, &pszLinkLocalPath);
\r
1265 if((ret == 0) && (bVolNum != bLinkVolNum))
\r
1270 #if REDCONF_VOLUME_COUNT > 1U
\r
1273 ret = RedCoreVolSetCurrent(bVolNum);
\r
1281 ret = RedPathLookup(pszLocalPath, &ulInode);
\r
1285 const char *pszLinkName;
\r
1286 uint32_t ulLinkPInode;
\r
1288 ret = RedPathToName(pszLinkLocalPath, &ulLinkPInode, &pszLinkName);
\r
1292 ret = RedCoreLink(ulLinkPInode, pszLinkName, ulInode);
\r
1301 return PosixReturn(ret);
\r
1306 /** @brief Close a file descriptor.
\r
1308 @param iFildes The file descriptor to close.
\r
1310 @return On success, zero is returned. On error, -1 is returned and
\r
1311 #red_errno is set appropriately.
\r
1313 <b>Errno values</b>
\r
1314 - #RED_EBADF: @p iFildes is not a valid file descriptor.
\r
1315 - #RED_EIO: A disk I/O error occurred.
\r
1316 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1318 int32_t red_close(
\r
1323 ret = PosixEnter();
\r
1326 ret = FildesClose(iFildes);
\r
1331 return PosixReturn(ret);
\r
1335 /** @brief Read from an open file.
\r
1337 The read takes place at the file offset associated with @p iFildes and
\r
1338 advances the file offset by the number of bytes actually read.
\r
1340 Data which has not yet been written, but which is before the end-of-file
\r
1341 (sparse data), will read as zeroes. A short read -- where the number of
\r
1342 bytes read is less than requested -- indicates that the requested read was
\r
1343 partially or, if zero bytes were read, entirely beyond the end-of-file.
\r
1345 @param iFildes The file descriptor from which to read.
\r
1346 @param pBuffer The buffer to populate with data read. Must be at least
\r
1347 @p ulLength bytes in size.
\r
1348 @param ulLength Number of bytes to attempt to read.
\r
1350 @return On success, returns a nonnegative value indicating the number of
\r
1351 bytes actually read. On error, -1 is returned and #red_errno is
\r
1352 set appropriately.
\r
1354 <b>Errno values</b>
\r
1355 - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
\r
1357 - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and
\r
1358 cannot be returned properly.
\r
1359 - #RED_EIO: A disk I/O error occurred.
\r
1360 - #RED_EISDIR: The @p iFildes is a file descriptor for a directory.
\r
1361 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1366 uint32_t ulLength)
\r
1368 uint32_t ulLenRead = 0U;
\r
1372 if(ulLength > (uint32_t)INT32_MAX)
\r
1374 ret = -RED_EINVAL;
\r
1378 ret = PosixEnter();
\r
1383 REDHANDLE *pHandle;
\r
1385 ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);
\r
1387 if((ret == 0) && ((pHandle->bFlags & HFLAG_READABLE) == 0U))
\r
1392 #if REDCONF_VOLUME_COUNT > 1U
\r
1395 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1401 ulLenRead = ulLength;
\r
1402 ret = RedCoreFileRead(pHandle->ulInode, pHandle->ullOffset, &ulLenRead, pBuffer);
\r
1407 REDASSERT(ulLenRead <= ulLength);
\r
1409 pHandle->ullOffset += ulLenRead;
\r
1417 iReturn = (int32_t)ulLenRead;
\r
1421 iReturn = PosixReturn(ret);
\r
1428 #if REDCONF_READ_ONLY == 0
\r
1429 /** @brief Write to an open file.
\r
1431 The write takes place at the file offset associated with @p iFildes and
\r
1432 advances the file offset by the number of bytes actually written.
\r
1433 Alternatively, if @p iFildes was opened with #RED_O_APPEND, the file offset
\r
1434 is set to the end-of-file before the write begins, and likewise advances by
\r
1435 the number of bytes actually written.
\r
1437 A short write -- where the number of bytes written is less than requested
\r
1438 -- indicates either that the file system ran out of space but was still
\r
1439 able to write some of the request; or that the request would have caused
\r
1440 the file to exceed the maximum file size, but some of the data could be
\r
1441 written prior to the file size limit.
\r
1443 If an error is returned (-1), either none of the data was written or a
\r
1444 critical error occurred (like an I/O error) and the file system volume will
\r
1447 @param iFildes The file descriptor to write to.
\r
1448 @param pBuffer The buffer containing the data to be written. Must be at
\r
1449 least @p ulLength bytes in size.
\r
1450 @param ulLength Number of bytes to attempt to write.
\r
1452 @return On success, returns a nonnegative value indicating the number of
\r
1453 bytes actually written. On error, -1 is returned and #red_errno is
\r
1454 set appropriately.
\r
1456 <b>Errno values</b>
\r
1457 - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
\r
1458 for writing. This includes the case where the file descriptor is for a
\r
1460 - #RED_EFBIG: No data can be written to the current file offset since the
\r
1461 resulting file size would exceed the maximum file size.
\r
1462 - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and
\r
1463 cannot be returned properly.
\r
1464 - #RED_EIO: A disk I/O error occurred.
\r
1465 - #RED_ENOSPC: No data can be written because there is insufficient free
\r
1467 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1469 int32_t red_write(
\r
1471 const void *pBuffer,
\r
1472 uint32_t ulLength)
\r
1474 uint32_t ulLenWrote = 0U;
\r
1478 if(ulLength > (uint32_t)INT32_MAX)
\r
1480 ret = -RED_EINVAL;
\r
1484 ret = PosixEnter();
\r
1489 REDHANDLE *pHandle;
\r
1491 ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);
\r
1492 if(ret == -RED_EISDIR)
\r
1494 /* POSIX says that if a file descriptor is not writable, the
\r
1495 errno should be -RED_EBADF. Directory file descriptors are
\r
1496 never writable, and unlike for read(), the spec does not
\r
1497 list -RED_EISDIR as an allowed errno. Therefore -RED_EBADF
\r
1503 if((ret == 0) && ((pHandle->bFlags & HFLAG_WRITEABLE) == 0U))
\r
1508 #if REDCONF_VOLUME_COUNT > 1U
\r
1511 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1515 if((ret == 0) && ((pHandle->bFlags & HFLAG_APPENDING) != 0U))
\r
1519 ret = RedCoreStat(pHandle->ulInode, &s);
\r
1522 pHandle->ullOffset = s.st_size;
\r
1528 ulLenWrote = ulLength;
\r
1529 ret = RedCoreFileWrite(pHandle->ulInode, pHandle->ullOffset, &ulLenWrote, pBuffer);
\r
1534 REDASSERT(ulLenWrote <= ulLength);
\r
1536 pHandle->ullOffset += ulLenWrote;
\r
1544 iReturn = (int32_t)ulLenWrote;
\r
1548 iReturn = PosixReturn(ret);
\r
1556 #if REDCONF_READ_ONLY == 0
\r
1557 /** @brief Synchronizes changes to a file.
\r
1559 Commits all changes associated with a file or directory (including file
\r
1560 data, directory contents, and metadata) to permanent storage. This
\r
1561 function will not return until the operation is complete.
\r
1563 In the current implementation, this function has global effect. All dirty
\r
1564 buffers are flushed and a transaction point is committed. Fsyncing one
\r
1565 file effectively fsyncs all files.
\r
1567 If fsync automatic transactions have been disabled, this function does
\r
1568 nothing and returns success. In the current implementation, this is the
\r
1569 only real difference between this function and red_transact(): this
\r
1570 function can be configured to do nothing, whereas red_transact() is
\r
1573 Applications written for portability should avoid assuming red_fsync()
\r
1574 effects all files, and use red_fsync() on each file that needs to be
\r
1577 Passing read-only file descriptors to this function is permitted.
\r
1579 @param iFildes The file descriptor to synchronize.
\r
1581 @return On success, zero is returned. On error, -1 is returned and
\r
1582 #red_errno is set appropriately.
\r
1584 <b>Errno values</b>
\r
1585 - #RED_EBADF: The @p iFildes argument is not a valid file descriptor.
\r
1586 - #RED_EIO: A disk I/O error occurred.
\r
1587 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1589 int32_t red_fsync(
\r
1594 ret = PosixEnter();
\r
1597 REDHANDLE *pHandle;
\r
1599 ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);
\r
1601 #if REDCONF_VOLUME_COUNT > 1U
\r
1604 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1608 /* No core event for fsync, so this transaction flag needs to be
\r
1613 uint32_t ulTransMask;
\r
1615 ret = RedCoreTransMaskGet(&ulTransMask);
\r
1617 if((ret == 0) && ((ulTransMask & RED_TRANSACT_FSYNC) != 0U))
\r
1619 ret = RedCoreVolTransact();
\r
1626 return PosixReturn(ret);
\r
1631 /** @brief Move the read/write file offset.
\r
1633 The file offset of the @p iFildes file descriptor is set to @p llOffset,
\r
1634 relative to some starting position. The available positions are:
\r
1636 - ::RED_SEEK_SET Seek from the start of the file. In other words,
\r
1637 @p llOffset becomes the new file offset.
\r
1638 - ::RED_SEEK_CUR Seek from the current file offset. In other words,
\r
1639 @p llOffset is added to the current file offset.
\r
1640 - ::RED_SEEK_END Seek from the end-of-file. In other words, the new file
\r
1641 offset is the file size plus @p llOffset.
\r
1643 Since @p llOffset is signed (can be negative), it is possible to seek
\r
1644 backward with ::RED_SEEK_CUR or ::RED_SEEK_END.
\r
1646 It is permitted to seek beyond the end-of-file; this does not increase the
\r
1647 file size (a subsequent red_write() call would).
\r
1649 Unlike POSIX lseek, this function cannot be used with directory file
\r
1652 @param iFildes The file descriptor whose offset is to be updated.
\r
1653 @param llOffset The new file offset, relative to @p whence.
\r
1654 @param whence The location from which @p llOffset should be applied.
\r
1656 @return On success, returns the new file position, measured in bytes from
\r
1657 the beginning of the file. On error, -1 is returned and #red_errno
\r
1658 is set appropriately.
\r
1660 <b>Errno values</b>
\r
1661 - #RED_EBADF: The @p iFildes argument is not an open file descriptor.
\r
1662 - #RED_EINVAL: @p whence is not a valid `RED_SEEK_` value; or the resulting
\r
1663 file offset would be negative or beyond the maximum file size.
\r
1664 - #RED_EIO: A disk I/O error occurred.
\r
1665 - #RED_EISDIR: The @p iFildes argument is a file descriptor for a directory.
\r
1666 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1668 int64_t red_lseek(
\r
1674 int64_t llReturn = -1; /* Init'd to quiet warnings. */
\r
1676 ret = PosixEnter();
\r
1679 int64_t llFrom = 0; /* Init'd to quiet warnings. */
\r
1680 REDHANDLE *pHandle;
\r
1682 /* Unlike POSIX, we disallow lseek() on directory handles.
\r
1684 ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);
\r
1686 #if REDCONF_VOLUME_COUNT > 1U
\r
1689 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1697 /* Seek from the beginning of the file.
\r
1699 case RED_SEEK_SET:
\r
1703 /* Seek from the current file offset.
\r
1705 case RED_SEEK_CUR:
\r
1706 REDASSERT(pHandle->ullOffset <= (uint64_t)INT64_MAX);
\r
1707 llFrom = (int64_t)pHandle->ullOffset;
\r
1710 /* Seek from the end of the file.
\r
1712 case RED_SEEK_END:
\r
1716 ret = RedCoreStat(pHandle->ulInode, &s);
\r
1719 REDASSERT(s.st_size <= (uint64_t)INT64_MAX);
\r
1720 llFrom = (int64_t)s.st_size;
\r
1727 ret = -RED_EINVAL;
\r
1734 REDASSERT(llFrom >= 0);
\r
1736 /* Avoid signed integer overflow from llFrom + llOffset with large
\r
1737 values of llOffset and nonzero llFrom values. Underflow isn't
\r
1738 possible since llFrom is nonnegative.
\r
1740 if((llOffset > 0) && (((uint64_t)llFrom + (uint64_t)llOffset) > (uint64_t)INT64_MAX))
\r
1742 ret = -RED_EINVAL;
\r
1746 int64_t llNewOffset = llFrom + llOffset;
\r
1748 if((llNewOffset < 0) || ((uint64_t)llNewOffset > gpRedVolume->ullMaxInodeSize))
\r
1750 /* Invalid file offset.
\r
1752 ret = -RED_EINVAL;
\r
1756 pHandle->ullOffset = (uint64_t)llNewOffset;
\r
1757 llReturn = llNewOffset;
\r
1767 llReturn = PosixReturn(ret);
\r
1774 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FTRUNCATE == 1)
\r
1775 /** @brief Truncate a file to a specified length.
\r
1777 Allows the file size to be increased, decreased, or to remain the same. If
\r
1778 the file size is increased, the new area is sparse (will read as zeroes).
\r
1779 If the file size is decreased, the data beyond the new end-of-file will
\r
1780 return to free space once it is no longer part of the committed state
\r
1781 (either immediately or after the next transaction point).
\r
1783 The value of the file offset is not modified by this function.
\r
1785 Unlike POSIX ftruncate, this function can fail when the disk is full if
\r
1786 @p ullSize is non-zero. If decreasing the file size, this can be fixed by
\r
1787 transacting and trying again: Reliance Edge guarantees that it is possible
\r
1788 to perform a truncate of at least one file that decreases the file size
\r
1789 after a transaction point. If disk full transactions are enabled, this will
\r
1790 happen automatically.
\r
1792 @param iFildes The file descriptor of the file to truncate.
\r
1793 @param ullSize The new size of the file.
\r
1795 @return On success, zero is returned. On error, -1 is returned and
\r
1796 #red_errno is set appropriately.
\r
1798 <b>Errno values</b>
\r
1799 - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open
\r
1800 for writing. This includes the case where the file descriptor is for a
\r
1802 - #RED_EFBIG: @p ullSize exceeds the maximum file size.
\r
1803 - #RED_EIO: A disk I/O error occurred.
\r
1804 - #RED_ENOSPC: Insufficient free space to perform the truncate.
\r
1805 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1807 int32_t red_ftruncate(
\r
1813 ret = PosixEnter();
\r
1816 REDHANDLE *pHandle;
\r
1818 ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);
\r
1819 if(ret == -RED_EISDIR)
\r
1821 /* Similar to red_write() (see comment there), the RED_EBADF error
\r
1822 for a non-writable file descriptor takes precedence.
\r
1827 if((ret == 0) && ((pHandle->bFlags & HFLAG_WRITEABLE) == 0U))
\r
1832 #if REDCONF_VOLUME_COUNT > 1U
\r
1835 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1841 ret = RedCoreFileTruncate(pHandle->ulInode, ullSize);
\r
1847 return PosixReturn(ret);
\r
1852 /** @brief Get the status of a file or directory.
\r
1854 See the ::REDSTAT type for the details of the information returned.
\r
1856 @param iFildes An open file descriptor for the file whose information is
\r
1858 @param pStat Pointer to a ::REDSTAT buffer to populate.
\r
1860 @return On success, zero is returned. On error, -1 is returned and
\r
1861 #red_errno is set appropriately.
\r
1863 <b>Errno values</b>
\r
1864 - #RED_EBADF: The @p iFildes argument is not a valid file descriptor.
\r
1865 - #RED_EINVAL: @p pStat is `NULL`.
\r
1866 - #RED_EIO: A disk I/O error occurred.
\r
1867 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1869 int32_t red_fstat(
\r
1875 ret = PosixEnter();
\r
1878 REDHANDLE *pHandle;
\r
1880 ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);
\r
1882 #if REDCONF_VOLUME_COUNT > 1U
\r
1885 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
1891 ret = RedCoreStat(pHandle->ulInode, pStat);
\r
1897 return PosixReturn(ret);
\r
1901 #if REDCONF_API_POSIX_READDIR == 1
\r
1902 /** @brief Open a directory stream for reading.
\r
1904 @param pszPath The path of the directory to open.
\r
1906 @return On success, returns a pointer to a ::REDDIR object that can be used
\r
1907 with red_readdir() and red_closedir(). On error, returns `NULL`
\r
1908 and #red_errno is set appropriately.
\r
1910 <b>Errno values</b>
\r
1911 - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is
\r
1913 - #RED_EIO: A disk I/O error occurred.
\r
1914 - #RED_ENOENT: A component of @p pszPath does not exist; or the @p pszPath
\r
1915 argument, after removing the volume prefix, points to an empty string.
\r
1916 - #RED_ENOTDIR: A component of @p pszPath is a not a directory.
\r
1917 - #RED_EMFILE: There are no available file descriptors.
\r
1918 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1920 REDDIR *red_opendir(
\r
1921 const char *pszPath)
\r
1925 REDDIR *pDir = NULL;
\r
1927 ret = PosixEnter();
\r
1930 ret = FildesOpen(pszPath, RED_O_RDONLY, FTYPE_DIR, &iFildes);
\r
1933 uint16_t uHandleIdx;
\r
1935 FildesUnpack(iFildes, &uHandleIdx, NULL, NULL);
\r
1936 pDir = &gaHandle[uHandleIdx];
\r
1942 REDASSERT((pDir == NULL) == (ret != 0));
\r
1953 /** @brief Read from a directory stream.
\r
1955 The ::REDDIRENT pointer returned by this function will be overwritten by
\r
1956 subsequent calls on the same @p pDir. Calls with other ::REDDIR objects
\r
1957 will *not* modify the returned ::REDDIRENT.
\r
1959 If files are added to the directory after it is opened, the new files may
\r
1960 or may not be returned by this function. If files are deleted, the deleted
\r
1961 files will not be returned.
\r
1963 This function (like its POSIX equivalent) returns `NULL` in two cases: on
\r
1964 error and when the end of the directory is reached. To distinguish between
\r
1965 these two cases, the application should set #red_errno to zero before
\r
1966 calling this function, and if `NULL` is returned, check if #red_errno is
\r
1967 still zero. If it is, the end of the directory was reached; otherwise,
\r
1968 there was an error.
\r
1970 @param pDirStream The directory stream to read from.
\r
1972 @return On success, returns a pointer to a ::REDDIRENT object which is
\r
1973 populated with directory entry information read from the directory.
\r
1974 On error, returns `NULL` and #red_errno is set appropriately. If at
\r
1975 the end of the directory, returns `NULL` but #red_errno is not
\r
1978 <b>Errno values</b>
\r
1979 - #RED_EBADF: @p pDirStream is not an open directory stream.
\r
1980 - #RED_EIO: A disk I/O error occurred.
\r
1981 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
1983 REDDIRENT *red_readdir(
\r
1984 REDDIR *pDirStream)
\r
1987 REDDIRENT *pDirEnt = NULL;
\r
1989 ret = PosixEnter();
\r
1992 if(!DirStreamIsValid(pDirStream))
\r
1996 #if REDCONF_VOLUME_COUNT > 1U
\r
1999 ret = RedCoreVolSetCurrent(pDirStream->bVolNum);
\r
2005 uint32_t ulDirPosition;
\r
2007 /* To save memory, the directory position is stored in the same
\r
2008 location as the file offset. This would be a bit cleaner using
\r
2009 a union, but MISRA-C:2012 Rule 19.2 disallows unions.
\r
2011 REDASSERT(pDirStream->ullOffset <= UINT32_MAX);
\r
2012 ulDirPosition = (uint32_t)pDirStream->ullOffset;
\r
2014 ret = RedCoreDirRead(pDirStream->ulInode, &ulDirPosition, pDirStream->dirent.d_name, &pDirStream->dirent.d_ino);
\r
2016 pDirStream->ullOffset = ulDirPosition;
\r
2020 /* POSIX extension: return stat information with the dirent.
\r
2022 ret = RedCoreStat(pDirStream->dirent.d_ino, &pDirStream->dirent.d_stat);
\r
2025 pDirEnt = &pDirStream->dirent;
\r
2028 else if(ret == -RED_ENOENT)
\r
2030 /* Reached the end of the directory; return NULL but do not set
\r
2037 /* Miscellaneous error; return NULL and set errno (done below).
\r
2047 REDASSERT(pDirEnt == NULL);
\r
2056 /** @brief Rewind a directory stream to read it from the beginning.
\r
2058 Similar to closing the directory object and opening it again, but without
\r
2059 the need for the path.
\r
2061 Since this function (like its POSIX equivalent) cannot return an error,
\r
2062 it takes no action in error conditions, such as when @p pDirStream is
\r
2065 @param pDirStream The directory stream to rewind.
\r
2067 void red_rewinddir(
\r
2068 REDDIR *pDirStream)
\r
2070 if(PosixEnter() == 0)
\r
2072 if(DirStreamIsValid(pDirStream))
\r
2074 pDirStream->ullOffset = 0U;
\r
2082 /** @brief Close a directory stream.
\r
2084 After calling this function, @p pDirStream should no longer be used.
\r
2086 @param pDirStream The directory stream to close.
\r
2088 @return On success, zero is returned. On error, -1 is returned and
\r
2089 #red_errno is set appropriately.
\r
2091 <b>Errno values</b>
\r
2092 - #RED_EBADF: @p pDirStream is not an open directory stream.
\r
2093 - #RED_EUSERS: Cannot become a file system user: too many users.
\r
2095 int32_t red_closedir(
\r
2096 REDDIR *pDirStream)
\r
2100 ret = PosixEnter();
\r
2103 if(DirStreamIsValid(pDirStream))
\r
2105 /* Mark this handle as unused.
\r
2107 pDirStream->ulInode = INODE_INVALID;
\r
2117 return PosixReturn(ret);
\r
2119 #endif /* REDCONF_API_POSIX_READDIR */
\r
2122 /** @brief Pointer to where the last file system error (errno) is stored.
\r
2124 This function is intended to be used via the #red_errno macro, or a similar
\r
2125 user-defined macro, that can be used both as an lvalue (writable) and an
\r
2126 rvalue (readable).
\r
2128 Under normal circumstances, the errno for each task is stored in a
\r
2129 different location. Applications do not need to worry about one task
\r
2130 obliterating an error value that another task needed to read. This task
\r
2131 errno for is initially zero. When one of the POSIX-like APIs returns an
\r
2132 indication of error, the location for the calling task will be populated
\r
2133 with the error value.
\r
2135 In some circumstances, this function will return a pointer to a global
\r
2136 errno location which is shared by multiple tasks. If the calling task is
\r
2137 not registered as a file system user and all of the task slots are full,
\r
2138 there can be no task-specific errno, so the global pointer is returned.
\r
2139 Likewise, if the file system driver is uninitialized, there are no
\r
2140 registered file system users and this function always returns the pointer
\r
2141 to the global errno. Under these circumstances, multiple tasks
\r
2142 manipulating errno could be problematic.
\r
2144 This function never returns `NULL` under any circumstances. The #red_errno
\r
2145 macro unconditionally dereferences the return value from this function, so
\r
2146 returning `NULL` could result in a fault.
\r
2148 @return Pointer to where the errno value is stored for this task.
\r
2150 REDSTATUS *red_errnoptr(void)
\r
2152 /* The global errno value, used in single-task configurations and when the
\r
2153 caller is not (and cannot become) a file system user (which includes
\r
2154 when the driver is uninitialized).
\r
2156 static REDSTATUS iGlobalErrno = 0;
\r
2158 #if REDCONF_TASK_COUNT == 1U
\r
2160 return &iGlobalErrno;
\r
2164 REDSTATUS *piErrno;
\r
2168 uint32_t ulTaskId = RedOsTaskId();
\r
2171 REDASSERT(ulTaskId != 0U);
\r
2173 /* If this task has used the file system before, it will already have
\r
2174 a task slot, which includes the task-specific errno.
\r
2176 RedOsMutexAcquire();
\r
2178 for(ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++)
\r
2180 if(gaTask[ulIdx].ulTaskId == ulTaskId)
\r
2186 RedOsMutexRelease();
\r
2188 if(ulIdx == REDCONF_TASK_COUNT)
\r
2192 /* This task is not a file system user, so try to register it as
\r
2193 one. This FS mutex must be held in order to register.
\r
2195 RedOsMutexAcquire();
\r
2197 ret = TaskRegister(&ulIdx);
\r
2199 RedOsMutexRelease();
\r
2203 REDASSERT(gaTask[ulIdx].ulTaskId == RedOsTaskId());
\r
2204 REDASSERT(gaTask[ulIdx].iErrno == 0);
\r
2206 piErrno = &gaTask[ulIdx].iErrno;
\r
2210 /* Unable to register; use the global errno.
\r
2212 piErrno = &iGlobalErrno;
\r
2217 piErrno = &gaTask[ulIdx].iErrno;
\r
2222 /* There are no registered file system tasks when the driver is
\r
2223 uninitialized, so use the global errno.
\r
2225 piErrno = &iGlobalErrno;
\r
2228 /* This function is not allowed to return NULL.
\r
2230 REDASSERT(piErrno != NULL);
\r
2237 /*-------------------------------------------------------------------
\r
2239 -------------------------------------------------------------------*/
\r
2241 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1))
\r
2243 /** @brief Remove a link to a file or directory.
\r
2245 If the link count becomes zero, the file or directory is deleted.
\r
2247 @param pszPath Path of the link to remove.
\r
2248 @param type The expected type of the path: file, directory, or either.
\r
2249 An error is returned if the expected type is file or
\r
2250 directory and does not match the path.
\r
2252 @return A negated ::REDSTATUS code indicating the operation result.
\r
2254 @retval -RED_EBUSY @p pszPath points to an inode with open handles
\r
2255 and a link count of one.
\r
2256 @retval -RED_EINVAL @p pszPath is `NULL`; or the volume containing
\r
2257 the path is not mounted.
\r
2258 @retval -RED_EIO A disk I/O error occurred.
\r
2259 @retval -RED_EISDIR @p type is ::FTYPE_FILE and the path names a
\r
2261 @retval -RED_ENAMETOOLONG @p pszName is too long.
\r
2262 @retval -RED_ENOENT The path does not name an existing file; or
\r
2263 @p pszPath, after removing the volume prefix,
\r
2264 points to an empty string.
\r
2265 @retval -RED_ENOTDIR @p type is ::FTYPE_DIR and the path does not
\r
2267 @retval -RED_ENOTEMPTY @p pszPath is a directory which is not empty.
\r
2268 @retval -RED_ENOSPC The file system does not have enough space to
\r
2269 modify the parent directory to perform the
\r
2272 static REDSTATUS UnlinkSub(
\r
2273 const char *pszPath,
\r
2277 const char *pszLocalPath;
\r
2280 ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);
\r
2282 #if REDCONF_VOLUME_COUNT > 1U
\r
2285 ret = RedCoreVolSetCurrent(bVolNum);
\r
2291 const char *pszName;
\r
2292 uint32_t ulPInode;
\r
2294 ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);
\r
2300 ret = RedCoreLookup(ulPInode, pszName, &ulInode);
\r
2302 /* ModeTypeCheck() always passes when the type is FTYPE_EITHER, so
\r
2303 skip stat'ing the inode in that case.
\r
2305 if((ret == 0) && (type != FTYPE_EITHER))
\r
2307 REDSTAT InodeStat;
\r
2309 ret = RedCoreStat(ulInode, &InodeStat);
\r
2312 ret = ModeTypeCheck(InodeStat.st_mode, type);
\r
2318 ret = InodeUnlinkCheck(ulInode);
\r
2323 ret = RedCoreUnlink(ulPInode, pszName);
\r
2330 #endif /* (REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) */
\r
2333 /** @brief Get a file descriptor for a path.
\r
2335 @param pszPath Path to a file to open.
\r
2336 @param ulOpenMode The RED_O_* flags the descriptor is opened with.
\r
2337 @param type Indicates the expected descriptor type: file, directory,
\r
2339 @param piFildes On successful return, populated with the file
\r
2342 @return A negated ::REDSTATUS code indicating the operation result.
\r
2344 @retval 0 Operation was successful.
\r
2345 @retval -RED_EINVAL @p piFildes is `NULL`; or @p pszPath is `NULL`;
\r
2346 or the volume is not mounted.
\r
2347 @retval -RED_EMFILE There are no available handles.
\r
2348 @retval -RED_EEXIST Using #RED_O_CREAT and #RED_O_EXCL, and the
\r
2349 indicated path already exists.
\r
2350 @retval -RED_EISDIR The path names a directory and @p ulOpenMode
\r
2351 includes #RED_O_WRONLY or #RED_O_RDWR.
\r
2352 @retval -RED_ENOENT #RED_O_CREAT is not set and the named file does
\r
2353 not exist; or #RED_O_CREAT is set and the parent
\r
2354 directory does not exist; or the volume does not
\r
2355 exist; or the @p pszPath argument, after
\r
2356 removing the volume prefix, points to an empty
\r
2358 @retval -RED_EIO A disk I/O error occurred.
\r
2359 @retval -RED_ENAMETOOLONG The length of a component of @p pszPath is
\r
2360 longer than #REDCONF_NAME_MAX.
\r
2361 @retval -RED_ENFILE Attempting to create a file but the file system
\r
2362 has used all available inode slots.
\r
2363 @retval -RED_ENOSPC The file does not exist and #RED_O_CREAT was
\r
2364 specified, but there is insufficient free space
\r
2365 to expand the directory or to create the new
\r
2367 @retval -RED_ENOTDIR A component of the prefix in @p pszPath does not
\r
2369 @retval -RED_EROFS The path resides on a read-only file system and
\r
2370 a write operation was requested.
\r
2372 static REDSTATUS FildesOpen(
\r
2373 const char *pszPath,
\r
2374 uint32_t ulOpenMode,
\r
2376 int32_t *piFildes)
\r
2379 const char *pszLocalPath;
\r
2382 ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);
\r
2386 if(piFildes == NULL)
\r
2388 ret = -RED_EINVAL;
\r
2390 #if REDCONF_READ_ONLY == 0
\r
2391 else if(gaRedVolume[bVolNum].fReadOnly && (ulOpenMode != RED_O_RDONLY))
\r
2398 uint16_t uHandleIdx;
\r
2399 REDHANDLE *pHandle = NULL;
\r
2401 /* Search for an unused handle.
\r
2403 for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)
\r
2405 if(gaHandle[uHandleIdx].ulInode == INODE_INVALID)
\r
2407 pHandle = &gaHandle[uHandleIdx];
\r
2412 /* Error if all the handles are in use.
\r
2414 if(pHandle == NULL)
\r
2416 ret = -RED_EMFILE;
\r
2420 bool fCreated = false;
\r
2421 uint16_t uMode = 0U;
\r
2422 uint32_t ulInode = 0U; /* Init'd to quiet warnings. */
\r
2424 #if REDCONF_VOLUME_COUNT > 1U
\r
2425 ret = RedCoreVolSetCurrent(bVolNum);
\r
2429 #if REDCONF_READ_ONLY == 0
\r
2430 if((ulOpenMode & RED_O_CREAT) != 0U)
\r
2432 uint32_t ulPInode;
\r
2433 const char *pszName;
\r
2435 ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);
\r
2438 ret = RedCoreCreate(ulPInode, pszName, false, &ulInode);
\r
2443 else if((ret == -RED_EEXIST) && ((ulOpenMode & RED_O_EXCL) == 0U))
\r
2445 /* If the path already exists and that's OK,
\r
2446 lookup its inode number.
\r
2448 ret = RedCoreLookup(ulPInode, pszName, &ulInode);
\r
2452 /* No action, just propagate the error.
\r
2460 ret = RedPathLookup(pszLocalPath, &ulInode);
\r
2464 /* If we created the inode, none of the below stuff is
\r
2465 necessary. This is important from an error handling
\r
2466 perspective -- we do not need code to delete the created
\r
2475 ret = RedCoreStat(ulInode, &s);
\r
2478 uMode = s.st_mode;
\r
2482 /* Error if the inode is not of the expected type.
\r
2486 ret = ModeTypeCheck(uMode, type);
\r
2489 /* Directories must always be opened with O_RDONLY.
\r
2491 if((ret == 0) && RED_S_ISDIR(uMode) && ((ulOpenMode & RED_O_RDONLY) == 0U))
\r
2493 ret = -RED_EISDIR;
\r
2496 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FTRUNCATE == 1)
\r
2497 if((ret == 0) && ((ulOpenMode & RED_O_TRUNC) != 0U))
\r
2499 ret = RedCoreFileTruncate(ulInode, UINT64_SUFFIX(0));
\r
2508 RedMemSet(pHandle, 0U, sizeof(*pHandle));
\r
2510 /* Populate this handle, marking it as in use.
\r
2512 pHandle->ulInode = ulInode;
\r
2513 pHandle->bVolNum = bVolNum;
\r
2515 if(RED_S_ISDIR(uMode))
\r
2517 pHandle->bFlags |= HFLAG_DIRECTORY;
\r
2520 if(((ulOpenMode & RED_O_RDONLY) != 0U) || ((ulOpenMode & RED_O_RDWR) != 0U))
\r
2522 pHandle->bFlags |= HFLAG_READABLE;
\r
2525 #if REDCONF_READ_ONLY == 0
\r
2526 if(((ulOpenMode & RED_O_WRONLY) != 0U) || ((ulOpenMode & RED_O_RDWR) != 0U))
\r
2528 pHandle->bFlags |= HFLAG_WRITEABLE;
\r
2531 if((ulOpenMode & RED_O_APPEND) != 0U)
\r
2533 pHandle->bFlags |= HFLAG_APPENDING;
\r
2537 iFildes = FildesPack(uHandleIdx, bVolNum);
\r
2540 /* It should be impossible to get here, unless there
\r
2541 is memory corruption.
\r
2544 ret = -RED_EFUBAR;
\r
2548 *piFildes = iFildes;
\r
2559 /** @brief Close a file descriptor.
\r
2561 @param iFildes The file descriptor to close.
\r
2563 @return A negated ::REDSTATUS code indicating the operation result.
\r
2565 @retval 0 Operation was successful.
\r
2566 @retval -RED_EBADF @p iFildes is not a valid file descriptor.
\r
2567 @retval -RED_EIO A disk I/O error occurred.
\r
2569 static REDSTATUS FildesClose(
\r
2572 REDHANDLE *pHandle;
\r
2575 ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);
\r
2577 #if REDCONF_READ_ONLY == 0
\r
2578 #if REDCONF_VOLUME_COUNT > 1U
\r
2581 ret = RedCoreVolSetCurrent(pHandle->bVolNum);
\r
2585 /* No core event for close, so this transaction flag needs to be
\r
2590 uint32_t ulTransMask;
\r
2592 ret = RedCoreTransMaskGet(&ulTransMask);
\r
2594 if((ret == 0) && ((ulTransMask & RED_TRANSACT_CLOSE) != 0U))
\r
2596 ret = RedCoreVolTransact();
\r
2603 /* Mark this handle as unused.
\r
2605 pHandle->ulInode = INODE_INVALID;
\r
2612 /** @brief Convert a file descriptor into a handle pointer.
\r
2614 Also validates the file descriptor.
\r
2616 @param iFildes The file descriptor for which to get a handle.
\r
2617 @param expectedType The expected type of the file descriptor: ::FTYPE_DIR,
\r
2618 ::FTYPE_FILE, or ::FTYPE_EITHER.
\r
2619 @param ppHandle On successful return, populated with a pointer to the
\r
2620 handle associated with @p iFildes.
\r
2622 @return A negated ::REDSTATUS code indicating the operation result.
\r
2624 @retval 0 Operation was successful.
\r
2625 @retval -RED_EBADF @p iFildes is not a valid file descriptor.
\r
2626 @retval -RED_EINVAL @p ppHandle is `NULL`.
\r
2627 @retval -RED_EISDIR Expected a file, but the file descriptor is for a
\r
2629 @retval -RED_ENOTDIR Expected a directory, but the file descriptor is for
\r
2632 static REDSTATUS FildesToHandle(
\r
2634 FTYPE expectedType,
\r
2635 REDHANDLE **ppHandle)
\r
2639 if(ppHandle == NULL)
\r
2642 ret = -RED_EINVAL;
\r
2644 else if(iFildes < FD_MIN)
\r
2650 uint16_t uHandleIdx;
\r
2652 uint16_t uGeneration;
\r
2654 FildesUnpack(iFildes, &uHandleIdx, &bVolNum, &uGeneration);
\r
2656 if( (uHandleIdx >= REDCONF_HANDLE_COUNT)
\r
2657 || (bVolNum >= REDCONF_VOLUME_COUNT)
\r
2658 || (gaHandle[uHandleIdx].ulInode == INODE_INVALID)
\r
2659 || (gaHandle[uHandleIdx].bVolNum != bVolNum)
\r
2660 || (gauGeneration[bVolNum] != uGeneration))
\r
2664 else if((expectedType == FTYPE_FILE) && ((gaHandle[uHandleIdx].bFlags & HFLAG_DIRECTORY) != 0U))
\r
2666 ret = -RED_EISDIR;
\r
2668 else if((expectedType == FTYPE_DIR) && ((gaHandle[uHandleIdx].bFlags & HFLAG_DIRECTORY) == 0U))
\r
2670 ret = -RED_ENOTDIR;
\r
2674 *ppHandle = &gaHandle[uHandleIdx];
\r
2683 /** @brief Pack a file descriptor.
\r
2685 @param uHandleIdx The index of the file handle that will be associated
\r
2686 with this file descriptor.
\r
2687 @param bVolNum The volume which contains the file or directory this
\r
2688 file descriptor was opened against.
\r
2690 @return The packed file descriptor.
\r
2692 static int32_t FildesPack(
\r
2693 uint16_t uHandleIdx,
\r
2698 if((uHandleIdx >= REDCONF_HANDLE_COUNT) || (bVolNum >= REDCONF_VOLUME_COUNT))
\r
2705 uint32_t ulFdBits;
\r
2707 REDASSERT(gauGeneration[bVolNum] <= FD_GEN_MAX);
\r
2708 REDASSERT(gauGeneration[bVolNum] != 0U);
\r
2710 ulFdBits = gauGeneration[bVolNum];
\r
2711 ulFdBits <<= FD_VOL_BITS;
\r
2712 ulFdBits |= bVolNum;
\r
2713 ulFdBits <<= FD_IDX_BITS;
\r
2714 ulFdBits |= uHandleIdx;
\r
2716 iFildes = (int32_t)ulFdBits;
\r
2718 if(iFildes < FD_MIN)
\r
2729 /** @brief Unpack a file descriptor.
\r
2731 @param iFildes The file descriptor to unpack.
\r
2732 @param puHandleIdx If non-NULL, populated with the handle index extracted
\r
2733 from the file descriptor.
\r
2734 @param pbVolNum If non-NULL, populated with the volume number extracted
\r
2735 from the file descriptor.
\r
2736 @param puGeneration If non-NULL, populated with the generation number
\r
2737 extracted from the file descriptor.
\r
2739 static void FildesUnpack(
\r
2741 uint16_t *puHandleIdx,
\r
2742 uint8_t *pbVolNum,
\r
2743 uint16_t *puGeneration)
\r
2745 uint32_t ulFdBits = (uint32_t)iFildes;
\r
2747 REDASSERT(iFildes >= FD_MIN);
\r
2749 if(puHandleIdx != NULL)
\r
2751 *puHandleIdx = (uint16_t)(ulFdBits & FD_IDX_MAX);
\r
2754 ulFdBits >>= FD_IDX_BITS;
\r
2756 if(pbVolNum != NULL)
\r
2758 *pbVolNum = (uint8_t)(ulFdBits & FD_VOL_MAX);
\r
2761 ulFdBits >>= FD_VOL_BITS;
\r
2763 if(puGeneration != NULL)
\r
2765 *puGeneration = (uint16_t)(ulFdBits & FD_GEN_MAX);
\r
2770 #if REDCONF_API_POSIX_READDIR == 1
\r
2771 /** @brief Validate a directory stream object.
\r
2773 @param pDirStream The directory stream to validate.
\r
2775 @return Whether the directory stream is valid.
\r
2777 @retval true The directory stream object appears valid.
\r
2778 @retval false The directory stream object is invalid.
\r
2780 static bool DirStreamIsValid(
\r
2781 const REDDIR *pDirStream)
\r
2785 if(pDirStream == NULL)
\r
2791 uint16_t uHandleIdx;
\r
2793 /* pDirStream should be a pointer to one of the handles.
\r
2795 A good compiler will optimize this loop into a bounds check and an
\r
2798 for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)
\r
2800 if(pDirStream == &gaHandle[uHandleIdx])
\r
2806 if(uHandleIdx < REDCONF_HANDLE_COUNT)
\r
2808 /* The handle must be in use, have a valid volume number, and be a
\r
2811 if( (pDirStream->ulInode == INODE_INVALID)
\r
2812 || (pDirStream->bVolNum >= REDCONF_VOLUME_COUNT)
\r
2813 || ((pDirStream->bFlags & HFLAG_DIRECTORY) == 0U))
\r
2820 /* pDirStream is a non-null pointer, but it is not a pointer to one
\r
2832 /** @brief Enter the file system driver.
\r
2834 @return A negated ::REDSTATUS code indicating the operation result.
\r
2836 @retval 0 Operation was successful.
\r
2837 @retval -RED_EINVAL The file system driver is uninitialized.
\r
2838 @retval -RED_EUSERS Cannot become a file system user: too many users.
\r
2840 static REDSTATUS PosixEnter(void)
\r
2846 #if REDCONF_TASK_COUNT > 1U
\r
2847 RedOsMutexAcquire();
\r
2849 ret = TaskRegister(NULL);
\r
2852 RedOsMutexRelease();
\r
2860 ret = -RED_EINVAL;
\r
2867 /** @brief Leave the file system driver.
\r
2869 static void PosixLeave(void)
\r
2871 /* If the driver was uninitialized, PosixEnter() should have failed and we
\r
2872 should not be calling PosixLeave().
\r
2874 REDASSERT(gfPosixInited);
\r
2876 #if REDCONF_TASK_COUNT > 1U
\r
2877 RedOsMutexRelease();
\r
2882 /** @brief Check that a mode is consistent with the given expected type.
\r
2884 @param uMode An inode mode, indicating whether the inode is a file
\r
2886 @param expectedType The expected type: ::FTYPE_FILE, ::FTYPE_DIR, or
\r
2889 @return A negated ::REDSTATUS code indicating the operation result.
\r
2891 @retval 0 Operation was successful.
\r
2892 @retval -RED_EISDIR Expected type is file, actual type is directory.
\r
2893 @retval -RED_ENOTDIR Expected type is directory, actual type is file.
\r
2895 static REDSTATUS ModeTypeCheck(
\r
2897 FTYPE expectedType)
\r
2901 if((expectedType == FTYPE_FILE) && RED_S_ISDIR(uMode))
\r
2903 /* Expected file, found directory.
\r
2905 ret = -RED_EISDIR;
\r
2907 else if((expectedType == FTYPE_DIR) && RED_S_ISREG(uMode))
\r
2909 /* Expected directory, found file.
\r
2911 ret = -RED_ENOTDIR;
\r
2915 /* No expected type or found what we expected.
\r
2924 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) || ((REDCONF_API_POSIX_RENAME == 1) && (REDCONF_RENAME_ATOMIC == 1)))
\r
2925 /** @brief Check whether an inode can be unlinked.
\r
2927 If an inode has a link count of 1 (meaning unlinking another name would
\r
2928 result in the deletion of the inode) and open handles, it cannot be deleted
\r
2929 since this would break open handles.
\r
2931 @param ulInode The inode whose name is to be unlinked.
\r
2933 @return A negated ::REDSTATUS code indicating the operation result.
\r
2935 @retval 0 Operation was successful.
\r
2936 @retval -RED_EBADF @p ulInode is not a valid inode.
\r
2937 @retval -RED_EBUSY The inode has a link count of one and open handles.
\r
2938 @retval -RED_EIO A disk I/O error occurred.
\r
2940 static REDSTATUS InodeUnlinkCheck(
\r
2943 uint16_t uHandleIdx;
\r
2946 #if REDCONF_API_POSIX_LINK == 0
\r
2949 REDSTAT InodeStat;
\r
2951 ret = RedCoreStat(ulInode, &InodeStat);
\r
2953 /* We only need to check for open handles if the inode is down to its last
\r
2954 link. If it has multiple links, the inode will continue to exist, so
\r
2955 deleting the name will not break the open handles.
\r
2957 if((ret == 0) && (InodeStat.st_nlink == 1U))
\r
2960 for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)
\r
2962 if((gaHandle[uHandleIdx].ulInode == ulInode) && (gaHandle[uHandleIdx].bVolNum == gbRedVolNum))
\r
2975 #if REDCONF_TASK_COUNT > 1U
\r
2976 /** @brief Register a task as a file system user, if it is not already
\r
2977 registered as one.
\r
2979 The caller must hold the FS mutex.
\r
2981 @param pulTaskIdx On successful return, if non-NULL, populated with the
\r
2982 index of the task slot assigned to the calling task.
\r
2983 This is populated whether or not the task had already
\r
2986 @return A negated ::REDSTATUS code indicating the operation result.
\r
2988 @retval 0 Operation was successful.
\r
2989 @retval -RED_EUSERS Cannot become a file system user: too many users.
\r
2991 static REDSTATUS TaskRegister(
\r
2992 uint32_t *pulTaskIdx)
\r
2994 uint32_t ulTaskId = RedOsTaskId();
\r
2995 uint32_t ulFirstFreeIdx = REDCONF_TASK_COUNT;
\r
2999 REDASSERT(ulTaskId != 0U);
\r
3001 /* Scan the task slots to determine if the task is registered as a file
\r
3004 for(ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++)
\r
3006 if(gaTask[ulIdx].ulTaskId == ulTaskId)
\r
3011 if((ulFirstFreeIdx == REDCONF_TASK_COUNT) && (gaTask[ulIdx].ulTaskId == 0U))
\r
3013 ulFirstFreeIdx = ulIdx;
\r
3017 if(ulIdx == REDCONF_TASK_COUNT)
\r
3019 /* Task not already registered.
\r
3021 if(ulFirstFreeIdx == REDCONF_TASK_COUNT)
\r
3023 /* Cannot register task, no more slots.
\r
3025 ret = -RED_EUSERS;
\r
3029 /* Registering task.
\r
3031 ulIdx = ulFirstFreeIdx;
\r
3032 gaTask[ulIdx].ulTaskId = ulTaskId;
\r
3038 /* Task already registered.
\r
3043 if((ret == 0) && (pulTaskIdx != NULL))
\r
3045 *pulTaskIdx = ulIdx;
\r
3050 #endif /* REDCONF_TASK_COUNT > 1U */
\r
3053 /** @brief Convert an error value into a simple 0 or -1 return.
\r
3055 This function is simple, but what it does is needed in many places. It
\r
3056 returns zero if @p iError is zero (meaning success) or it returns -1 if
\r
3057 @p iError is nonzero (meaning error). Also, if @p iError is nonzero, it
\r
3058 is saved in red_errno.
\r
3060 @param iError The error value.
\r
3062 @return Returns 0 if @p iError is 0; otherwise, returns -1.
\r
3064 static int32_t PosixReturn(
\r
3077 /* The errors should be negative, and errno positive.
\r
3079 REDASSERT(iError < 0);
\r
3080 red_errno = -iError;
\r
3087 #endif /* REDCONF_API_POSIX == 1 */
\r