]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/posix/posix.c
34c49869f8ad70e71b510d9a705f9ef9f42258c0
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / posix / posix.c
1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
2 \r
3                    Copyright (c) 2014-2015 Datalight, Inc.\r
4                        All Rights Reserved Worldwide.\r
5 \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
9 \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
14 \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
18 */\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
23     more information.\r
24 */\r
25 /** @file\r
26     @brief Implementation of the the Reliance Edge POSIX-like API.\r
27 */\r
28 \r
29 #include <redfs.h>\r
30 \r
31 #if REDCONF_API_POSIX == 1\r
32 \r
33 /** @defgroup red_group_posix The POSIX-like File System Interface\r
34     @{\r
35 */\r
36 \r
37 #include <redvolume.h>\r
38 #include <redcoreapi.h>\r
39 #include <redposix.h>\r
40 #include "redpath.h"\r
41 \r
42 \r
43 /*-------------------------------------------------------------------\r
44     File Descriptors\r
45 -------------------------------------------------------------------*/\r
46 \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
50 \r
51 /*  31 bits available: file descriptors are int32_t, but the sign bit must\r
52     always be zero.\r
53 */\r
54 #if (FD_GEN_BITS + FD_VOL_BITS + FD_IDX_BITS) > 31U\r
55   #error "Internal error: too many file descriptor bits!"\r
56 #endif\r
57 \r
58 /*  Maximum values for file descriptor components.\r
59 */\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
63 \r
64 #if REDCONF_VOLUME_COUNT > FD_VOL_MAX\r
65   #error "Error: Too many file system volumes!"\r
66 #endif\r
67 #if REDCONF_HANDLE_COUNT > (FD_IDX_MAX + 1U)\r
68   #error "Error: Too many file system handles!"\r
69 #endif\r
70 \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
73 */\r
74 #define FD_MIN  (3)\r
75 \r
76 /*-------------------------------------------------------------------\r
77     Handles\r
78 -------------------------------------------------------------------*/\r
79 \r
80 /*  Mask of all RED_O_* values.\r
81 */\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
83 \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
88 \r
89 /*  @brief Handle structure, used to implement file descriptors and directory\r
90            streams.\r
91 */\r
92 typedef struct sREDHANDLE\r
93 {\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
100   #endif\r
101 } REDHANDLE;\r
102 \r
103 /*-------------------------------------------------------------------\r
104     Tasks\r
105 -------------------------------------------------------------------*/\r
106 \r
107 #if REDCONF_TASK_COUNT > 1U\r
108 /*  @brief Per-task information.\r
109 */\r
110 typedef struct\r
111 {\r
112     uint32_t    ulTaskId;   /**< ID of the task which owns this slot; 0 if free. */\r
113     REDSTATUS   iErrno;     /**< Last error value. */\r
114 } TASKSLOT;\r
115 #endif\r
116 \r
117 /*-------------------------------------------------------------------\r
118     Local Prototypes\r
119 -------------------------------------------------------------------*/\r
120 \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
123 #endif\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
131 #endif\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
137 #endif\r
138 #if REDCONF_TASK_COUNT > 1U\r
139 static REDSTATUS TaskRegister(uint32_t *pulTaskIdx);\r
140 #endif\r
141 static int32_t PosixReturn(REDSTATUS iError);\r
142 \r
143 /*-------------------------------------------------------------------\r
144     Globals\r
145 -------------------------------------------------------------------*/\r
146 \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
151 #endif\r
152 \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
158 */\r
159 static uint16_t gauGeneration[REDCONF_VOLUME_COUNT];\r
160 \r
161 \r
162 /*-------------------------------------------------------------------\r
163     Public API\r
164 -------------------------------------------------------------------*/\r
165 \r
166 /** @brief Initialize the Reliance Edge file system driver.\r
167 \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
171 \r
172     If this function is called when the Reliance Edge driver is already\r
173     initialized, it does nothing and returns success.\r
174 \r
175     This function is not thread safe: attempting to initialize from multiple\r
176     threads could leave things in a bad state.\r
177 \r
178     @return On success, zero is returned.  On error, -1 is returned and\r
179             #red_errno is set appropriately.\r
180 \r
181     <b>Errno values</b>\r
182     - #RED_EINVAL: The volume path prefix configuration is invalid.\r
183 */\r
184 int32_t red_init(void)\r
185 {\r
186     REDSTATUS ret;\r
187 \r
188     if(gfPosixInited)\r
189     {\r
190         ret = 0;\r
191     }\r
192     else\r
193     {\r
194         ret = RedCoreInit();\r
195         if(ret == 0)\r
196         {\r
197             RedMemSet(gaHandle, 0U, sizeof(gaHandle));\r
198 \r
199           #if REDCONF_TASK_COUNT > 1U\r
200             RedMemSet(gaTask, 0U, sizeof(gaTask));\r
201           #endif\r
202 \r
203             gfPosixInited = true;\r
204         }\r
205     }\r
206 \r
207     return PosixReturn(ret);\r
208 }\r
209 \r
210 \r
211 /** @brief Uninitialize the Reliance Edge file system driver.\r
212 \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
216 \r
217     If this function is called when the Reliance Edge driver is already\r
218     uninitialized, it does nothing and returns success.\r
219 \r
220     This function is not thread safe: attempting to uninitialize from multiple\r
221     threads could leave things in a bad state.\r
222 \r
223     @return On success, zero is returned.  On error, -1 is returned and\r
224             #red_errno is set appropriately.\r
225 \r
226     <b>Errno values</b>\r
227     - #RED_EBUSY: At least one volume is still mounted.\r
228 */\r
229 int32_t red_uninit(void)\r
230 {\r
231     REDSTATUS ret;\r
232 \r
233     if(gfPosixInited)\r
234     {\r
235         ret = PosixEnter();\r
236 \r
237         if(ret == 0)\r
238         {\r
239             uint8_t bVolNum;\r
240 \r
241             for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)\r
242             {\r
243                 if(gaRedVolume[bVolNum].fMounted)\r
244                 {\r
245                     ret = -RED_EBUSY;\r
246                     break;\r
247                 }\r
248             }\r
249 \r
250             if(ret == 0)\r
251             {\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
256                 */\r
257                 gfPosixInited = false;\r
258             }\r
259 \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
263 \r
264                 Don't use PosixLeave(), since it asserts gfPosixInited is true.\r
265             */\r
266           #if REDCONF_TASK_COUNT > 1U\r
267             RedOsMutexRelease();\r
268           #endif\r
269         }\r
270 \r
271         if(ret == 0)\r
272         {\r
273             ret = RedCoreUninit();\r
274 \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
278             */\r
279             REDASSERT(ret == 0);\r
280         }\r
281     }\r
282     else\r
283     {\r
284         ret = 0;\r
285     }\r
286 \r
287     return PosixReturn(ret);\r
288 }\r
289 \r
290 \r
291 /** @brief Mount a file system volume.\r
292 \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
296 \r
297     An error is returned if the volume is already mounted.\r
298 \r
299     @param pszVolume    A path prefix identifying the volume to mount.\r
300 \r
301     @return On success, zero is returned.  On error, -1 is returned and\r
302             #red_errno is set appropriately.\r
303 \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
310 */\r
311 int32_t red_mount(\r
312     const char *pszVolume)\r
313 {\r
314     REDSTATUS   ret;\r
315 \r
316     ret = PosixEnter();\r
317 \r
318     if(ret == 0)\r
319     {\r
320         uint8_t bVolNum;\r
321 \r
322         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
323 \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
326         */\r
327         if((ret == 0) && gaRedVolume[bVolNum].fMounted)\r
328         {\r
329             ret = -RED_EBUSY;\r
330         }\r
331 \r
332       #if REDCONF_VOLUME_COUNT > 1U\r
333         if(ret == 0)\r
334         {\r
335             ret = RedCoreVolSetCurrent(bVolNum);\r
336         }\r
337       #endif\r
338 \r
339         if(ret == 0)\r
340         {\r
341             ret = RedCoreVolMount();\r
342         }\r
343 \r
344         if(ret == 0)\r
345         {\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
350                 manually.\r
351             */\r
352             gauGeneration[bVolNum]++;\r
353             if(gauGeneration[bVolNum] > FD_GEN_MAX)\r
354             {\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
361                 */\r
362                 gauGeneration[bVolNum] = 1U;\r
363             }\r
364         }\r
365 \r
366         PosixLeave();\r
367     }\r
368 \r
369     return PosixReturn(ret);\r
370 }\r
371 \r
372 \r
373 /** @brief Unmount a file system volume.\r
374 \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
378 \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
383 \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
389     unmount will fail.\r
390 \r
391     An error is returned if the volume is already unmounted.\r
392 \r
393     @param pszVolume    A path prefix identifying the volume to unmount.\r
394 \r
395     @return On success, zero is returned.  On error, -1 is returned and\r
396             #red_errno is set appropriately.\r
397 \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
405 */\r
406 int32_t red_umount(\r
407     const char *pszVolume)\r
408 {\r
409     REDSTATUS   ret;\r
410 \r
411     ret = PosixEnter();\r
412     if(ret == 0)\r
413     {\r
414         uint8_t bVolNum;\r
415 \r
416         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
417 \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
420         */\r
421         if((ret == 0) && !gaRedVolume[bVolNum].fMounted)\r
422         {\r
423             ret = -RED_EINVAL;\r
424         }\r
425 \r
426         if(ret == 0)\r
427         {\r
428             uint16_t    uHandleIdx;\r
429 \r
430             /*  Do not unmount the volume if it still has open handles.\r
431             */\r
432             for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)\r
433             {\r
434                 const REDHANDLE *pHandle = &gaHandle[uHandleIdx];\r
435 \r
436                 if((pHandle->ulInode != INODE_INVALID) && (pHandle->bVolNum == bVolNum))\r
437                 {\r
438                     ret = -RED_EBUSY;\r
439                     break;\r
440                 }\r
441             }\r
442         }\r
443 \r
444       #if REDCONF_VOLUME_COUNT > 1U\r
445         if(ret == 0)\r
446         {\r
447             ret = RedCoreVolSetCurrent(bVolNum);\r
448         }\r
449       #endif\r
450 \r
451         if(ret == 0)\r
452         {\r
453             ret = RedCoreVolUnmount();\r
454         }\r
455 \r
456         PosixLeave();\r
457     }\r
458 \r
459     return PosixReturn(ret);\r
460 }\r
461 \r
462 \r
463 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1)\r
464 /** @brief Format a file system volume.\r
465 \r
466     Uses the statically defined volume configuration.  After calling this\r
467     function, the volume needs to be mounted -- see red_mount().\r
468 \r
469     An error is returned if the volume is mounted.\r
470 \r
471     @param pszVolume    A path prefix identifying the volume to format.\r
472 \r
473     @return On success, zero is returned.  On error, -1 is returned and\r
474             #red_errno is set appropriately.\r
475 \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
482 */\r
483 int32_t red_format(\r
484     const char *pszVolume)\r
485 {\r
486     REDSTATUS   ret;\r
487 \r
488     ret = PosixEnter();\r
489     if(ret == 0)\r
490     {\r
491         uint8_t bVolNum;\r
492 \r
493         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
494 \r
495       #if REDCONF_VOLUME_COUNT > 1U\r
496         if(ret == 0)\r
497         {\r
498             ret = RedCoreVolSetCurrent(bVolNum);\r
499         }\r
500       #endif\r
501 \r
502         if(ret == 0)\r
503         {\r
504             ret = RedCoreVolFormat();\r
505         }\r
506 \r
507         PosixLeave();\r
508     }\r
509 \r
510     return PosixReturn(ret);\r
511 }\r
512 #endif\r
513 \r
514 \r
515 #if REDCONF_READ_ONLY == 0\r
516 /** @brief Commit a transaction point.\r
517 \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
525 \r
526     @param pszVolume    A path prefix identifying the volume to transact.\r
527 \r
528     @return On success, zero is returned.  On error, -1 is returned and\r
529             #red_errno is set appropriately.\r
530 \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
536 */\r
537 int32_t red_transact(\r
538     const char *pszVolume)\r
539 {\r
540     REDSTATUS   ret;\r
541 \r
542     ret = PosixEnter();\r
543     if(ret == 0)\r
544     {\r
545         uint8_t bVolNum;\r
546 \r
547         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
548 \r
549       #if REDCONF_VOLUME_COUNT > 1U\r
550         if(ret == 0)\r
551         {\r
552             ret = RedCoreVolSetCurrent(bVolNum);\r
553         }\r
554       #endif\r
555 \r
556         if(ret == 0)\r
557         {\r
558             ret = RedCoreVolTransact();\r
559         }\r
560 \r
561         PosixLeave();\r
562     }\r
563 \r
564     return PosixReturn(ret);\r
565 }\r
566 #endif\r
567 \r
568 \r
569 #if REDCONF_READ_ONLY == 0\r
570 /** @brief Update the transaction mask.\r
571 \r
572     The following events are available:\r
573 \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
585 \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
589     functionality.\r
590 \r
591     Attempting to enable events for excluded functionality will result in an\r
592     error.\r
593 \r
594     @param pszVolume    The path prefix of the volume whose transaction mask is\r
595                         being changed.\r
596     @param ulEventMask  A bitwise-OR'd mask of automatic transaction events to\r
597                         be set as the current transaction mode.\r
598 \r
599     @return On success, zero is returned.  On error, -1 is returned and\r
600             #red_errno is set appropriately.\r
601 \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
607 */\r
608 int32_t red_settransmask(\r
609     const char *pszVolume,\r
610     uint32_t    ulEventMask)\r
611 {\r
612     REDSTATUS   ret;\r
613 \r
614     ret = PosixEnter();\r
615     if(ret == 0)\r
616     {\r
617         uint8_t bVolNum;\r
618 \r
619         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
620 \r
621       #if REDCONF_VOLUME_COUNT > 1U\r
622         if(ret == 0)\r
623         {\r
624             ret = RedCoreVolSetCurrent(bVolNum);\r
625         }\r
626       #endif\r
627 \r
628         if(ret == 0)\r
629         {\r
630             ret = RedCoreTransMaskSet(ulEventMask);\r
631         }\r
632 \r
633         PosixLeave();\r
634     }\r
635 \r
636     return PosixReturn(ret);\r
637 }\r
638 #endif\r
639 \r
640 \r
641 /** @brief Read the transaction mask.\r
642 \r
643     If the volume is read-only, the returned event mask is always zero.\r
644 \r
645     @param pszVolume    The path prefix of the volume whose transaction mask is\r
646                         being retrieved.\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
650 \r
651     @return On success, zero is returned.  On error, -1 is returned and\r
652             #red_errno is set appropriately.\r
653 \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
659 */\r
660 int32_t red_gettransmask(\r
661     const char *pszVolume,\r
662     uint32_t   *pulEventMask)\r
663 {\r
664     REDSTATUS   ret;\r
665 \r
666     ret = PosixEnter();\r
667     if(ret == 0)\r
668     {\r
669         uint8_t bVolNum;\r
670 \r
671         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
672 \r
673       #if REDCONF_VOLUME_COUNT > 1U\r
674         if(ret == 0)\r
675         {\r
676             ret = RedCoreVolSetCurrent(bVolNum);\r
677         }\r
678       #endif\r
679 \r
680         if(ret == 0)\r
681         {\r
682             ret = RedCoreTransMaskGet(pulEventMask);\r
683         }\r
684 \r
685         PosixLeave();\r
686     }\r
687 \r
688     return PosixReturn(ret);\r
689 }\r
690 \r
691 \r
692 /** @brief Query file system status information.\r
693 \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
696     valid path.\r
697 \r
698     @param pszVolume    The path prefix of the volume to query.\r
699     @param pStatvfs     The buffer to populate with volume information.\r
700 \r
701     @return On success, zero is returned.  On error, -1 is returned and\r
702             #red_errno is set appropriately.\r
703 \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
709 */\r
710 int32_t red_statvfs(\r
711     const char *pszVolume,\r
712     REDSTATFS  *pStatvfs)\r
713 {\r
714     REDSTATUS   ret;\r
715 \r
716     ret = PosixEnter();\r
717     if(ret == 0)\r
718     {\r
719         uint8_t bVolNum;\r
720 \r
721         ret = RedPathSplit(pszVolume, &bVolNum, NULL);\r
722 \r
723       #if REDCONF_VOLUME_COUNT > 1U\r
724         if(ret == 0)\r
725         {\r
726             ret = RedCoreVolSetCurrent(bVolNum);\r
727         }\r
728       #endif\r
729 \r
730         if(ret == 0)\r
731         {\r
732             ret = RedCoreVolStat(pStatvfs);\r
733         }\r
734 \r
735         PosixLeave();\r
736     }\r
737 \r
738     return PosixReturn(ret);\r
739 }\r
740 \r
741 \r
742 /** @brief Open a file or directory.\r
743 \r
744     Exactly one file access mode must be specified:\r
745 \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
749 \r
750     Directories can only be opened with `RED_O_RDONLY`.\r
751 \r
752     The following flags may also be used:\r
753 \r
754     - #RED_O_APPEND: Set the file offset to the end-of-file prior to each\r
755       write.\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
761 \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
764 \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
767 \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
770 \r
771     The returned file descriptor must later be closed with red_close().\r
772 \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
775     not supported.\r
776 \r
777     @param pszPath      The path to the file or directory.\r
778     @param ulOpenMode   The open flags (mask of `RED_O_` values).\r
779 \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
782 \r
783     <b>Errno values</b>\r
784     - #RED_EEXIST: Using #RED_O_CREAT and #RED_O_EXCL, and the indicated path\r
785       already exists.\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
793       #REDCONF_NAME_MAX.\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
802       new file.\r
803     - #RED_ENOTDIR: A component of the prefix in @p pszPath does not name a\r
804       directory.\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
808 */\r
809 int32_t red_open(\r
810     const char *pszPath,\r
811     uint32_t    ulOpenMode)\r
812 {\r
813     int32_t     iFildes = -1;   /* Init'd to quiet warnings. */\r
814     REDSTATUS   ret;\r
815 \r
816   #if REDCONF_READ_ONLY == 1\r
817     if(ulOpenMode != RED_O_RDONLY)\r
818     {\r
819         ret = -RED_EROFS;\r
820     }\r
821   #else\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
829     {\r
830         ret = -RED_EINVAL;\r
831     }\r
832   #if REDCONF_API_POSIX_FTRUNCATE == 0\r
833     else if((ulOpenMode & RED_O_TRUNC) != 0U)\r
834     {\r
835         ret = -RED_EINVAL;\r
836     }\r
837   #endif\r
838   #endif\r
839     else\r
840     {\r
841         ret = PosixEnter();\r
842     }\r
843 \r
844     if(ret == 0)\r
845     {\r
846         ret = FildesOpen(pszPath, ulOpenMode, FTYPE_EITHER, &iFildes);\r
847 \r
848         PosixLeave();\r
849     }\r
850 \r
851     if(ret != 0)\r
852     {\r
853         iFildes = PosixReturn(ret);\r
854     }\r
855 \r
856     return iFildes;\r
857 }\r
858 \r
859 \r
860 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_UNLINK == 1)\r
861 /** @brief Delete a file or directory.\r
862 \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
866 \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
871     the file is open.\r
872 \r
873     If the path names a directory which is not empty, the unlink will fail.\r
874 \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
877 \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
882 \r
883     @param pszPath  The path of the file or directory to delete.\r
884 \r
885     @return On success, zero is returned.  On error, -1 is returned and\r
886             #red_errno is set appropriately.\r
887 \r
888     <b>Errno values</b>\r
889     - #RED_EBUSY: @p pszPath points to an inode with open handles and a link\r
890       count of one.\r
891     - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is\r
892       not mounted.\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
895       #REDCONF_NAME_MAX.\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
903 */\r
904 int32_t red_unlink(\r
905     const char *pszPath)\r
906 {\r
907     REDSTATUS   ret;\r
908 \r
909     ret = PosixEnter();\r
910     if(ret == 0)\r
911     {\r
912         ret = UnlinkSub(pszPath, FTYPE_EITHER);\r
913 \r
914         PosixLeave();\r
915     }\r
916 \r
917     return PosixReturn(ret);\r
918 }\r
919 #endif\r
920 \r
921 \r
922 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_MKDIR == 1)\r
923 /** @brief Create a new directory.\r
924 \r
925     Unlike POSIX mkdir, this function has no second argument for the\r
926     permissions (which Reliance Edge does not use).\r
927 \r
928     @param pszPath  The name and location of the directory to create.\r
929 \r
930     @return On success, zero is returned.  On error, -1 is returned and\r
931             #red_errno is set appropriately.\r
932 \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
936       not mounted.\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
939       #REDCONF_NAME_MAX.\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
948 */\r
949 int32_t red_mkdir(\r
950     const char *pszPath)\r
951 {\r
952     REDSTATUS   ret;\r
953 \r
954     ret = PosixEnter();\r
955     if(ret == 0)\r
956     {\r
957         const char *pszLocalPath;\r
958         uint8_t     bVolNum;\r
959 \r
960         ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);\r
961 \r
962       #if REDCONF_VOLUME_COUNT > 1U\r
963         if(ret == 0)\r
964         {\r
965             ret = RedCoreVolSetCurrent(bVolNum);\r
966         }\r
967       #endif\r
968 \r
969         if(ret == 0)\r
970         {\r
971             const char *pszName;\r
972             uint32_t    ulPInode;\r
973 \r
974             ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);\r
975 \r
976             if(ret == 0)\r
977             {\r
978                 uint32_t ulInode;\r
979 \r
980                 ret = RedCoreCreate(ulPInode, pszName, true, &ulInode);\r
981             }\r
982         }\r
983 \r
984         PosixLeave();\r
985     }\r
986 \r
987     return PosixReturn(ret);\r
988 }\r
989 #endif\r
990 \r
991 \r
992 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RMDIR == 1)\r
993 /** @brief Delete a directory.\r
994 \r
995     The given directory name is deleted and the corresponding directory inode\r
996     will be deleted.\r
997 \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
1000 \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
1003     will fail.\r
1004 \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
1008 \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
1011 \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
1016 \r
1017     @param pszPath  The path of the directory to delete.\r
1018 \r
1019     @return On success, zero is returned.  On error, -1 is returned and\r
1020             #red_errno is set appropriately.\r
1021 \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
1025       not mounted.\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
1031       string.\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
1037       system.\r
1038     - #RED_EUSERS: Cannot become a file system user: too many users.\r
1039 */\r
1040 int32_t red_rmdir(\r
1041     const char *pszPath)\r
1042 {\r
1043     REDSTATUS   ret;\r
1044 \r
1045     ret = PosixEnter();\r
1046     if(ret == 0)\r
1047     {\r
1048         ret = UnlinkSub(pszPath, FTYPE_DIR);\r
1049 \r
1050         PosixLeave();\r
1051     }\r
1052 \r
1053     return PosixReturn(ret);\r
1054 }\r
1055 #endif\r
1056 \r
1057 \r
1058 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RENAME == 1)\r
1059 /** @brief Rename a file or directory.\r
1060 \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
1063 \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
1068 \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
1079 \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
1085 \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
1088 \r
1089     @return On success, zero is returned.  On error, -1 is returned and\r
1090             #red_errno is set appropriately.\r
1091 \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
1109       not empty.\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
1113       system.\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
1116       volumes.\r
1117 */\r
1118 int32_t red_rename(\r
1119     const char *pszOldPath,\r
1120     const char *pszNewPath)\r
1121 {\r
1122     REDSTATUS   ret;\r
1123 \r
1124     ret = PosixEnter();\r
1125     if(ret == 0)\r
1126     {\r
1127         const char *pszOldLocalPath;\r
1128         uint8_t     bOldVolNum;\r
1129 \r
1130         ret = RedPathSplit(pszOldPath, &bOldVolNum, &pszOldLocalPath);\r
1131 \r
1132         if(ret == 0)\r
1133         {\r
1134             const char *pszNewLocalPath;\r
1135             uint8_t     bNewVolNum;\r
1136 \r
1137             ret = RedPathSplit(pszNewPath, &bNewVolNum, &pszNewLocalPath);\r
1138 \r
1139             if((ret == 0) && (bOldVolNum != bNewVolNum))\r
1140             {\r
1141                 ret = -RED_EXDEV;\r
1142             }\r
1143 \r
1144           #if REDCONF_VOLUME_COUNT > 1U\r
1145             if(ret == 0)\r
1146             {\r
1147                 ret = RedCoreVolSetCurrent(bOldVolNum);\r
1148             }\r
1149           #endif\r
1150 \r
1151             if(ret == 0)\r
1152             {\r
1153                 const char *pszOldName;\r
1154                 uint32_t    ulOldPInode;\r
1155 \r
1156                 ret = RedPathToName(pszOldLocalPath, &ulOldPInode, &pszOldName);\r
1157 \r
1158                 if(ret == 0)\r
1159                 {\r
1160                     const char *pszNewName;\r
1161                     uint32_t    ulNewPInode;\r
1162 \r
1163                     ret = RedPathToName(pszNewLocalPath, &ulNewPInode, &pszNewName);\r
1164 \r
1165                   #if REDCONF_RENAME_ATOMIC == 1\r
1166                     if(ret == 0)\r
1167                     {\r
1168                         uint32_t ulDestInode;\r
1169 \r
1170                         ret = RedCoreLookup(ulNewPInode, pszNewName, &ulDestInode);\r
1171                         if(ret == 0)\r
1172                         {\r
1173                             ret = InodeUnlinkCheck(ulDestInode);\r
1174                         }\r
1175                         else if(ret == -RED_ENOENT)\r
1176                         {\r
1177                             ret = 0;\r
1178                         }\r
1179                         else\r
1180                         {\r
1181                             /*  Unexpected error, nothing to do.\r
1182                             */\r
1183                         }\r
1184                     }\r
1185                   #endif\r
1186 \r
1187                     if(ret == 0)\r
1188                     {\r
1189                         ret = RedCoreRename(ulOldPInode, pszOldName, ulNewPInode, pszNewName);\r
1190                     }\r
1191                 }\r
1192             }\r
1193         }\r
1194 \r
1195         PosixLeave();\r
1196     }\r
1197 \r
1198     return PosixReturn(ret);\r
1199 }\r
1200 #endif\r
1201 \r
1202 \r
1203 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_LINK == 1)\r
1204 /** @brief Create a hard link.\r
1205 \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
1212 \r
1213     If @p pszPath names a directory, the operation will fail.\r
1214 \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
1217 \r
1218     @return On success, zero is returned.  On error, -1 is returned and\r
1219             #red_errno is set appropriately.\r
1220 \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
1233       string.\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
1242       volumes.\r
1243 */\r
1244 int32_t red_link(\r
1245     const char *pszPath,\r
1246     const char *pszHardLink)\r
1247 {\r
1248     REDSTATUS   ret;\r
1249 \r
1250     ret = PosixEnter();\r
1251     if(ret == 0)\r
1252     {\r
1253         const char *pszLocalPath;\r
1254         uint8_t     bVolNum;\r
1255 \r
1256         ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);\r
1257 \r
1258         if(ret == 0)\r
1259         {\r
1260             const char *pszLinkLocalPath;\r
1261             uint8_t     bLinkVolNum;\r
1262 \r
1263             ret = RedPathSplit(pszHardLink, &bLinkVolNum, &pszLinkLocalPath);\r
1264 \r
1265             if((ret == 0) && (bVolNum != bLinkVolNum))\r
1266             {\r
1267                 ret = -RED_EXDEV;\r
1268             }\r
1269 \r
1270           #if REDCONF_VOLUME_COUNT > 1U\r
1271             if(ret == 0)\r
1272             {\r
1273                 ret = RedCoreVolSetCurrent(bVolNum);\r
1274             }\r
1275           #endif\r
1276 \r
1277             if(ret == 0)\r
1278             {\r
1279                 uint32_t    ulInode;\r
1280 \r
1281                 ret = RedPathLookup(pszLocalPath, &ulInode);\r
1282 \r
1283                 if(ret == 0)\r
1284                 {\r
1285                     const char *pszLinkName;\r
1286                     uint32_t    ulLinkPInode;\r
1287 \r
1288                     ret = RedPathToName(pszLinkLocalPath, &ulLinkPInode, &pszLinkName);\r
1289 \r
1290                     if(ret == 0)\r
1291                     {\r
1292                         ret = RedCoreLink(ulLinkPInode, pszLinkName, ulInode);\r
1293                     }\r
1294                 }\r
1295             }\r
1296         }\r
1297 \r
1298         PosixLeave();\r
1299     }\r
1300 \r
1301     return PosixReturn(ret);\r
1302 }\r
1303 #endif\r
1304 \r
1305 \r
1306 /** @brief Close a file descriptor.\r
1307 \r
1308     @param iFildes  The file descriptor to close.\r
1309 \r
1310     @return On success, zero is returned.  On error, -1 is returned and\r
1311             #red_errno is set appropriately.\r
1312 \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
1317 */\r
1318 int32_t red_close(\r
1319     int32_t     iFildes)\r
1320 {\r
1321     REDSTATUS   ret;\r
1322 \r
1323     ret = PosixEnter();\r
1324     if(ret == 0)\r
1325     {\r
1326         ret = FildesClose(iFildes);\r
1327 \r
1328         PosixLeave();\r
1329     }\r
1330 \r
1331     return PosixReturn(ret);\r
1332 }\r
1333 \r
1334 \r
1335 /** @brief Read from an open file.\r
1336 \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
1339 \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
1344 \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
1349 \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
1353 \r
1354     <b>Errno values</b>\r
1355     - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open\r
1356       for reading.\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
1362 */\r
1363 int32_t red_read(\r
1364     int32_t     iFildes,\r
1365     void       *pBuffer,\r
1366     uint32_t    ulLength)\r
1367 {\r
1368     uint32_t    ulLenRead = 0U;\r
1369     REDSTATUS   ret;\r
1370     int32_t     iReturn;\r
1371 \r
1372     if(ulLength > (uint32_t)INT32_MAX)\r
1373     {\r
1374         ret = -RED_EINVAL;\r
1375     }\r
1376     else\r
1377     {\r
1378         ret = PosixEnter();\r
1379     }\r
1380 \r
1381     if(ret == 0)\r
1382     {\r
1383         REDHANDLE  *pHandle;\r
1384 \r
1385         ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);\r
1386 \r
1387         if((ret == 0) && ((pHandle->bFlags & HFLAG_READABLE) == 0U))\r
1388         {\r
1389             ret = -RED_EBADF;\r
1390         }\r
1391 \r
1392       #if REDCONF_VOLUME_COUNT > 1U\r
1393         if(ret == 0)\r
1394         {\r
1395             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1396         }\r
1397       #endif\r
1398 \r
1399         if(ret == 0)\r
1400         {\r
1401             ulLenRead = ulLength;\r
1402             ret = RedCoreFileRead(pHandle->ulInode, pHandle->ullOffset, &ulLenRead, pBuffer);\r
1403         }\r
1404 \r
1405         if(ret == 0)\r
1406         {\r
1407             REDASSERT(ulLenRead <= ulLength);\r
1408 \r
1409             pHandle->ullOffset += ulLenRead;\r
1410         }\r
1411 \r
1412         PosixLeave();\r
1413     }\r
1414 \r
1415     if(ret == 0)\r
1416     {\r
1417         iReturn = (int32_t)ulLenRead;\r
1418     }\r
1419     else\r
1420     {\r
1421         iReturn = PosixReturn(ret);\r
1422     }\r
1423 \r
1424     return iReturn;\r
1425 }\r
1426 \r
1427 \r
1428 #if REDCONF_READ_ONLY == 0\r
1429 /** @brief Write to an open file.\r
1430 \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
1436 \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
1442 \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
1445     be read-only.\r
1446 \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
1451 \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
1455 \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
1459       directory.\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
1466       space.\r
1467     - #RED_EUSERS: Cannot become a file system user: too many users.\r
1468 */\r
1469 int32_t red_write(\r
1470     int32_t     iFildes,\r
1471     const void *pBuffer,\r
1472     uint32_t    ulLength)\r
1473 {\r
1474     uint32_t    ulLenWrote = 0U;\r
1475     REDSTATUS   ret;\r
1476     int32_t     iReturn;\r
1477 \r
1478     if(ulLength > (uint32_t)INT32_MAX)\r
1479     {\r
1480         ret = -RED_EINVAL;\r
1481     }\r
1482     else\r
1483     {\r
1484         ret = PosixEnter();\r
1485     }\r
1486 \r
1487     if(ret == 0)\r
1488     {\r
1489         REDHANDLE *pHandle;\r
1490 \r
1491         ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);\r
1492         if(ret == -RED_EISDIR)\r
1493         {\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
1498                 takes precedence.\r
1499             */\r
1500             ret = -RED_EBADF;\r
1501         }\r
1502 \r
1503         if((ret == 0) && ((pHandle->bFlags & HFLAG_WRITEABLE) == 0U))\r
1504         {\r
1505             ret = -RED_EBADF;\r
1506         }\r
1507 \r
1508       #if REDCONF_VOLUME_COUNT > 1U\r
1509         if(ret == 0)\r
1510         {\r
1511             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1512         }\r
1513       #endif\r
1514 \r
1515         if((ret == 0) && ((pHandle->bFlags & HFLAG_APPENDING) != 0U))\r
1516         {\r
1517             REDSTAT s;\r
1518 \r
1519             ret = RedCoreStat(pHandle->ulInode, &s);\r
1520             if(ret == 0)\r
1521             {\r
1522                 pHandle->ullOffset = s.st_size;\r
1523             }\r
1524         }\r
1525 \r
1526         if(ret == 0)\r
1527         {\r
1528             ulLenWrote = ulLength;\r
1529             ret = RedCoreFileWrite(pHandle->ulInode, pHandle->ullOffset, &ulLenWrote, pBuffer);\r
1530         }\r
1531 \r
1532         if(ret == 0)\r
1533         {\r
1534             REDASSERT(ulLenWrote <= ulLength);\r
1535 \r
1536             pHandle->ullOffset += ulLenWrote;\r
1537         }\r
1538 \r
1539         PosixLeave();\r
1540     }\r
1541 \r
1542     if(ret == 0)\r
1543     {\r
1544         iReturn = (int32_t)ulLenWrote;\r
1545     }\r
1546     else\r
1547     {\r
1548         iReturn = PosixReturn(ret);\r
1549     }\r
1550 \r
1551     return iReturn;\r
1552 }\r
1553 #endif\r
1554 \r
1555 \r
1556 #if REDCONF_READ_ONLY == 0\r
1557 /** @brief Synchronizes changes to a file.\r
1558 \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
1562 \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
1566 \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
1571     unconditional.\r
1572 \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
1575     synchronized.\r
1576 \r
1577     Passing read-only file descriptors to this function is permitted.\r
1578 \r
1579     @param iFildes  The file descriptor to synchronize.\r
1580 \r
1581     @return On success, zero is returned.  On error, -1 is returned and\r
1582             #red_errno is set appropriately.\r
1583 \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
1588 */\r
1589 int32_t red_fsync(\r
1590     int32_t     iFildes)\r
1591 {\r
1592     REDSTATUS   ret;\r
1593 \r
1594     ret = PosixEnter();\r
1595     if(ret == 0)\r
1596     {\r
1597         REDHANDLE *pHandle;\r
1598 \r
1599         ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);\r
1600 \r
1601       #if REDCONF_VOLUME_COUNT > 1U\r
1602         if(ret == 0)\r
1603         {\r
1604             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1605         }\r
1606       #endif\r
1607 \r
1608         /*  No core event for fsync, so this transaction flag needs to be\r
1609             implemented here.\r
1610         */\r
1611         if(ret == 0)\r
1612         {\r
1613             uint32_t    ulTransMask;\r
1614 \r
1615             ret = RedCoreTransMaskGet(&ulTransMask);\r
1616 \r
1617             if((ret == 0) && ((ulTransMask & RED_TRANSACT_FSYNC) != 0U))\r
1618             {\r
1619                 ret = RedCoreVolTransact();\r
1620             }\r
1621         }\r
1622 \r
1623         PosixLeave();\r
1624     }\r
1625 \r
1626     return PosixReturn(ret);\r
1627 }\r
1628 #endif\r
1629 \r
1630 \r
1631 /** @brief Move the read/write file offset.\r
1632 \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
1635 \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
1642 \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
1645 \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
1648 \r
1649     Unlike POSIX lseek, this function cannot be used with directory file\r
1650     descriptors.\r
1651 \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
1655 \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
1659 \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
1667 */\r
1668 int64_t red_lseek(\r
1669     int32_t     iFildes,\r
1670     int64_t     llOffset,\r
1671     REDWHENCE   whence)\r
1672 {\r
1673     REDSTATUS   ret;\r
1674     int64_t     llReturn = -1;  /* Init'd to quiet warnings. */\r
1675 \r
1676     ret = PosixEnter();\r
1677     if(ret == 0)\r
1678     {\r
1679         int64_t     llFrom = 0; /* Init'd to quiet warnings. */\r
1680         REDHANDLE  *pHandle;\r
1681 \r
1682         /*  Unlike POSIX, we disallow lseek() on directory handles.\r
1683         */\r
1684         ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);\r
1685 \r
1686       #if REDCONF_VOLUME_COUNT > 1U\r
1687         if(ret == 0)\r
1688         {\r
1689             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1690         }\r
1691       #endif\r
1692 \r
1693         if(ret == 0)\r
1694         {\r
1695             switch(whence)\r
1696             {\r
1697                 /*  Seek from the beginning of the file.\r
1698                 */\r
1699                 case RED_SEEK_SET:\r
1700                     llFrom = 0;\r
1701                     break;\r
1702 \r
1703                 /*  Seek from the current file offset.\r
1704                 */\r
1705                 case RED_SEEK_CUR:\r
1706                     REDASSERT(pHandle->ullOffset <= (uint64_t)INT64_MAX);\r
1707                     llFrom = (int64_t)pHandle->ullOffset;\r
1708                     break;\r
1709 \r
1710                 /*  Seek from the end of the file.\r
1711                 */\r
1712                 case RED_SEEK_END:\r
1713                 {\r
1714                     REDSTAT s;\r
1715 \r
1716                     ret = RedCoreStat(pHandle->ulInode, &s);\r
1717                     if(ret == 0)\r
1718                     {\r
1719                         REDASSERT(s.st_size <= (uint64_t)INT64_MAX);\r
1720                         llFrom = (int64_t)s.st_size;\r
1721                     }\r
1722 \r
1723                     break;\r
1724                 }\r
1725 \r
1726                 default:\r
1727                     ret = -RED_EINVAL;\r
1728                     break;\r
1729             }\r
1730         }\r
1731 \r
1732         if(ret == 0)\r
1733         {\r
1734             REDASSERT(llFrom >= 0);\r
1735 \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
1739             */\r
1740             if((llOffset > 0) && (((uint64_t)llFrom + (uint64_t)llOffset) > (uint64_t)INT64_MAX))\r
1741             {\r
1742                 ret = -RED_EINVAL;\r
1743             }\r
1744             else\r
1745             {\r
1746                 int64_t llNewOffset = llFrom + llOffset;\r
1747 \r
1748                 if((llNewOffset < 0) || ((uint64_t)llNewOffset > gpRedVolume->ullMaxInodeSize))\r
1749                 {\r
1750                     /*  Invalid file offset.\r
1751                     */\r
1752                     ret = -RED_EINVAL;\r
1753                 }\r
1754                 else\r
1755                 {\r
1756                     pHandle->ullOffset = (uint64_t)llNewOffset;\r
1757                     llReturn = llNewOffset;\r
1758                 }\r
1759             }\r
1760         }\r
1761 \r
1762         PosixLeave();\r
1763     }\r
1764 \r
1765     if(ret != 0)\r
1766     {\r
1767         llReturn = PosixReturn(ret);\r
1768     }\r
1769 \r
1770     return llReturn;\r
1771 }\r
1772 \r
1773 \r
1774 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FTRUNCATE == 1)\r
1775 /** @brief Truncate a file to a specified length.\r
1776 \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
1782 \r
1783     The value of the file offset is not modified by this function.\r
1784 \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
1791 \r
1792     @param iFildes  The file descriptor of the file to truncate.\r
1793     @param ullSize  The new size of the file.\r
1794 \r
1795     @return On success, zero is returned.  On error, -1 is returned and\r
1796             #red_errno is set appropriately.\r
1797 \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
1801       directory.\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
1806 */\r
1807 int32_t red_ftruncate(\r
1808     int32_t     iFildes,\r
1809     uint64_t    ullSize)\r
1810 {\r
1811     REDSTATUS   ret;\r
1812 \r
1813     ret = PosixEnter();\r
1814     if(ret == 0)\r
1815     {\r
1816         REDHANDLE *pHandle;\r
1817 \r
1818         ret = FildesToHandle(iFildes, FTYPE_FILE, &pHandle);\r
1819         if(ret == -RED_EISDIR)\r
1820         {\r
1821             /*  Similar to red_write() (see comment there), the RED_EBADF error\r
1822                 for a non-writable file descriptor takes precedence.\r
1823             */\r
1824             ret = -RED_EBADF;\r
1825         }\r
1826 \r
1827         if((ret == 0) && ((pHandle->bFlags & HFLAG_WRITEABLE) == 0U))\r
1828         {\r
1829             ret = -RED_EBADF;\r
1830         }\r
1831 \r
1832       #if REDCONF_VOLUME_COUNT > 1U\r
1833         if(ret == 0)\r
1834         {\r
1835             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1836         }\r
1837       #endif\r
1838 \r
1839         if(ret == 0)\r
1840         {\r
1841             ret = RedCoreFileTruncate(pHandle->ulInode, ullSize);\r
1842         }\r
1843 \r
1844         PosixLeave();\r
1845     }\r
1846 \r
1847     return PosixReturn(ret);\r
1848 }\r
1849 #endif\r
1850 \r
1851 \r
1852 /** @brief Get the status of a file or directory.\r
1853 \r
1854     See the ::REDSTAT type for the details of the information returned.\r
1855 \r
1856     @param iFildes  An open file descriptor for the file whose information is\r
1857                     to be retrieved.\r
1858     @param pStat    Pointer to a ::REDSTAT buffer to populate.\r
1859 \r
1860     @return On success, zero is returned.  On error, -1 is returned and\r
1861             #red_errno is set appropriately.\r
1862 \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
1868 */\r
1869 int32_t red_fstat(\r
1870     int32_t     iFildes,\r
1871     REDSTAT    *pStat)\r
1872 {\r
1873     REDSTATUS   ret;\r
1874 \r
1875     ret = PosixEnter();\r
1876     if(ret == 0)\r
1877     {\r
1878         REDHANDLE *pHandle;\r
1879 \r
1880         ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);\r
1881 \r
1882       #if REDCONF_VOLUME_COUNT > 1U\r
1883         if(ret == 0)\r
1884         {\r
1885             ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
1886         }\r
1887       #endif\r
1888 \r
1889         if(ret == 0)\r
1890         {\r
1891             ret = RedCoreStat(pHandle->ulInode, pStat);\r
1892         }\r
1893 \r
1894         PosixLeave();\r
1895     }\r
1896 \r
1897     return PosixReturn(ret);\r
1898 }\r
1899 \r
1900 \r
1901 #if REDCONF_API_POSIX_READDIR == 1\r
1902 /** @brief Open a directory stream for reading.\r
1903 \r
1904     @param pszPath  The path of the directory to open.\r
1905 \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
1909 \r
1910     <b>Errno values</b>\r
1911     - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is\r
1912       not mounted.\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
1919 */\r
1920 REDDIR *red_opendir(\r
1921     const char *pszPath)\r
1922 {\r
1923     int32_t     iFildes;\r
1924     REDSTATUS   ret;\r
1925     REDDIR     *pDir = NULL;\r
1926 \r
1927     ret = PosixEnter();\r
1928     if(ret == 0)\r
1929     {\r
1930         ret = FildesOpen(pszPath, RED_O_RDONLY, FTYPE_DIR, &iFildes);\r
1931         if(ret == 0)\r
1932         {\r
1933             uint16_t uHandleIdx;\r
1934 \r
1935             FildesUnpack(iFildes, &uHandleIdx, NULL, NULL);\r
1936             pDir = &gaHandle[uHandleIdx];\r
1937         }\r
1938 \r
1939         PosixLeave();\r
1940     }\r
1941 \r
1942     REDASSERT((pDir == NULL) == (ret != 0));\r
1943 \r
1944     if(pDir == NULL)\r
1945     {\r
1946         red_errno = -ret;\r
1947     }\r
1948 \r
1949     return pDir;\r
1950 }\r
1951 \r
1952 \r
1953 /** @brief Read from a directory stream.\r
1954 \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
1958 \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
1962 \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
1969 \r
1970     @param pDirStream   The directory stream to read from.\r
1971 \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
1976             modified.\r
1977 \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
1982 */\r
1983 REDDIRENT *red_readdir(\r
1984     REDDIR     *pDirStream)\r
1985 {\r
1986     REDSTATUS   ret;\r
1987     REDDIRENT  *pDirEnt = NULL;\r
1988 \r
1989     ret = PosixEnter();\r
1990     if(ret == 0)\r
1991     {\r
1992         if(!DirStreamIsValid(pDirStream))\r
1993         {\r
1994             ret = -RED_EBADF;\r
1995         }\r
1996       #if REDCONF_VOLUME_COUNT > 1U\r
1997         else\r
1998         {\r
1999             ret = RedCoreVolSetCurrent(pDirStream->bVolNum);\r
2000         }\r
2001       #endif\r
2002 \r
2003         if(ret == 0)\r
2004         {\r
2005             uint32_t ulDirPosition;\r
2006 \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
2010             */\r
2011             REDASSERT(pDirStream->ullOffset <= UINT32_MAX);\r
2012             ulDirPosition = (uint32_t)pDirStream->ullOffset;\r
2013 \r
2014             ret = RedCoreDirRead(pDirStream->ulInode, &ulDirPosition, pDirStream->dirent.d_name, &pDirStream->dirent.d_ino);\r
2015 \r
2016             pDirStream->ullOffset = ulDirPosition;\r
2017 \r
2018             if(ret == 0)\r
2019             {\r
2020                 /*  POSIX extension: return stat information with the dirent.\r
2021                 */\r
2022                 ret = RedCoreStat(pDirStream->dirent.d_ino, &pDirStream->dirent.d_stat);\r
2023                 if(ret == 0)\r
2024                 {\r
2025                     pDirEnt = &pDirStream->dirent;\r
2026                 }\r
2027             }\r
2028             else if(ret == -RED_ENOENT)\r
2029             {\r
2030                 /*  Reached the end of the directory; return NULL but do not set\r
2031                     errno.\r
2032                 */\r
2033                 ret = 0;\r
2034             }\r
2035             else\r
2036             {\r
2037                 /*  Miscellaneous error; return NULL and set errno (done below).\r
2038                 */\r
2039             }\r
2040         }\r
2041 \r
2042         PosixLeave();\r
2043     }\r
2044 \r
2045     if(ret != 0)\r
2046     {\r
2047         REDASSERT(pDirEnt == NULL);\r
2048 \r
2049         red_errno = -ret;\r
2050     }\r
2051 \r
2052     return pDirEnt;\r
2053 }\r
2054 \r
2055 \r
2056 /** @brief Rewind a directory stream to read it from the beginning.\r
2057 \r
2058     Similar to closing the directory object and opening it again, but without\r
2059     the need for the path.\r
2060 \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
2063     invalid.\r
2064 \r
2065     @param pDirStream   The directory stream to rewind.\r
2066 */\r
2067 void red_rewinddir(\r
2068     REDDIR *pDirStream)\r
2069 {\r
2070     if(PosixEnter() == 0)\r
2071     {\r
2072         if(DirStreamIsValid(pDirStream))\r
2073         {\r
2074             pDirStream->ullOffset = 0U;\r
2075         }\r
2076 \r
2077         PosixLeave();\r
2078     }\r
2079 }\r
2080 \r
2081 \r
2082 /** @brief Close a directory stream.\r
2083 \r
2084     After calling this function, @p pDirStream should no longer be used.\r
2085 \r
2086     @param pDirStream   The directory stream to close.\r
2087 \r
2088     @return On success, zero is returned.  On error, -1 is returned and\r
2089             #red_errno is set appropriately.\r
2090 \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
2094 */\r
2095 int32_t red_closedir(\r
2096     REDDIR     *pDirStream)\r
2097 {\r
2098     REDSTATUS   ret;\r
2099 \r
2100     ret = PosixEnter();\r
2101     if(ret == 0)\r
2102     {\r
2103         if(DirStreamIsValid(pDirStream))\r
2104         {\r
2105             /*  Mark this handle as unused.\r
2106             */\r
2107             pDirStream->ulInode = INODE_INVALID;\r
2108         }\r
2109         else\r
2110         {\r
2111             ret = -RED_EBADF;\r
2112         }\r
2113 \r
2114         PosixLeave();\r
2115     }\r
2116 \r
2117     return PosixReturn(ret);\r
2118 }\r
2119 #endif /* REDCONF_API_POSIX_READDIR */\r
2120 \r
2121 \r
2122 /** @brief Pointer to where the last file system error (errno) is stored.\r
2123 \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
2127 \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
2134 \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
2143 \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
2147 \r
2148     @return Pointer to where the errno value is stored for this task.\r
2149 */\r
2150 REDSTATUS *red_errnoptr(void)\r
2151 {\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
2155     */\r
2156     static REDSTATUS iGlobalErrno = 0;\r
2157 \r
2158   #if REDCONF_TASK_COUNT == 1U\r
2159 \r
2160     return &iGlobalErrno;\r
2161 \r
2162   #else\r
2163 \r
2164     REDSTATUS *piErrno;\r
2165 \r
2166     if(gfPosixInited)\r
2167     {\r
2168         uint32_t ulTaskId = RedOsTaskId();\r
2169         uint32_t ulIdx;\r
2170 \r
2171         REDASSERT(ulTaskId != 0U);\r
2172 \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
2175         */\r
2176         RedOsMutexAcquire();\r
2177 \r
2178         for(ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++)\r
2179         {\r
2180             if(gaTask[ulIdx].ulTaskId == ulTaskId)\r
2181             {\r
2182                 break;\r
2183             }\r
2184         }\r
2185 \r
2186         RedOsMutexRelease();\r
2187 \r
2188         if(ulIdx == REDCONF_TASK_COUNT)\r
2189         {\r
2190             REDSTATUS ret;\r
2191 \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
2194             */\r
2195             RedOsMutexAcquire();\r
2196 \r
2197             ret = TaskRegister(&ulIdx);\r
2198 \r
2199             RedOsMutexRelease();\r
2200 \r
2201             if(ret == 0)\r
2202             {\r
2203                 REDASSERT(gaTask[ulIdx].ulTaskId == RedOsTaskId());\r
2204                 REDASSERT(gaTask[ulIdx].iErrno == 0);\r
2205 \r
2206                 piErrno = &gaTask[ulIdx].iErrno;\r
2207             }\r
2208             else\r
2209             {\r
2210                 /*  Unable to register; use the global errno.\r
2211                 */\r
2212                 piErrno = &iGlobalErrno;\r
2213             }\r
2214         }\r
2215         else\r
2216         {\r
2217             piErrno = &gaTask[ulIdx].iErrno;\r
2218         }\r
2219     }\r
2220     else\r
2221     {\r
2222         /*  There are no registered file system tasks when the driver is\r
2223             uninitialized, so use the global errno.\r
2224         */\r
2225         piErrno = &iGlobalErrno;\r
2226     }\r
2227 \r
2228     /*  This function is not allowed to return NULL.\r
2229     */\r
2230     REDASSERT(piErrno != NULL);\r
2231     return piErrno;\r
2232 \r
2233   #endif\r
2234 }\r
2235 /** @} */\r
2236 \r
2237 /*-------------------------------------------------------------------\r
2238     Helper Functions\r
2239 -------------------------------------------------------------------*/\r
2240 \r
2241 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1))\r
2242 \r
2243 /** @brief Remove a link to a file or directory.\r
2244 \r
2245     If the link count becomes zero, the file or directory is deleted.\r
2246 \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
2251 \r
2252     @return A negated ::REDSTATUS code indicating the operation result.\r
2253 \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
2260                                 directory.\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
2266                                 name a directory.\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
2270                                 deletion.\r
2271 */\r
2272 static REDSTATUS UnlinkSub(\r
2273     const char *pszPath,\r
2274     FTYPE       type)\r
2275 {\r
2276     uint8_t     bVolNum;\r
2277     const char *pszLocalPath;\r
2278     REDSTATUS   ret;\r
2279 \r
2280     ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);\r
2281 \r
2282   #if REDCONF_VOLUME_COUNT > 1U\r
2283     if(ret == 0)\r
2284     {\r
2285         ret = RedCoreVolSetCurrent(bVolNum);\r
2286     }\r
2287   #endif\r
2288 \r
2289     if(ret == 0)\r
2290     {\r
2291         const char *pszName;\r
2292         uint32_t    ulPInode;\r
2293 \r
2294         ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);\r
2295 \r
2296         if(ret == 0)\r
2297         {\r
2298             uint32_t ulInode;\r
2299 \r
2300             ret = RedCoreLookup(ulPInode, pszName, &ulInode);\r
2301 \r
2302             /*  ModeTypeCheck() always passes when the type is FTYPE_EITHER, so\r
2303                 skip stat'ing the inode in that case.\r
2304             */\r
2305             if((ret == 0) && (type != FTYPE_EITHER))\r
2306             {\r
2307                 REDSTAT InodeStat;\r
2308 \r
2309                 ret = RedCoreStat(ulInode, &InodeStat);\r
2310                 if(ret == 0)\r
2311                 {\r
2312                     ret = ModeTypeCheck(InodeStat.st_mode, type);\r
2313                 }\r
2314             }\r
2315 \r
2316             if(ret == 0)\r
2317             {\r
2318                 ret = InodeUnlinkCheck(ulInode);\r
2319             }\r
2320 \r
2321             if(ret == 0)\r
2322             {\r
2323                 ret = RedCoreUnlink(ulPInode, pszName);\r
2324             }\r
2325         }\r
2326     }\r
2327 \r
2328     return ret;\r
2329 }\r
2330 #endif /* (REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) */\r
2331 \r
2332 \r
2333 /** @brief Get a file descriptor for a path.\r
2334 \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
2338                         or either.\r
2339     @param piFildes     On successful return, populated with the file\r
2340                         descriptor.\r
2341 \r
2342     @return A negated ::REDSTATUS code indicating the operation result.\r
2343 \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
2357                                 string.\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
2366                                 file.\r
2367     @retval -RED_ENOTDIR        A component of the prefix in @p pszPath does not\r
2368                                 name a directory.\r
2369     @retval -RED_EROFS          The path resides on a read-only file system and\r
2370                                 a write operation was requested.\r
2371 */\r
2372 static REDSTATUS FildesOpen(\r
2373     const char *pszPath,\r
2374     uint32_t    ulOpenMode,\r
2375     FTYPE       type,\r
2376     int32_t    *piFildes)\r
2377 {\r
2378     uint8_t     bVolNum;\r
2379     const char *pszLocalPath;\r
2380     REDSTATUS   ret;\r
2381 \r
2382     ret = RedPathSplit(pszPath, &bVolNum, &pszLocalPath);\r
2383 \r
2384     if(ret == 0)\r
2385     {\r
2386         if(piFildes == NULL)\r
2387         {\r
2388             ret = -RED_EINVAL;\r
2389         }\r
2390       #if REDCONF_READ_ONLY == 0\r
2391         else if(gaRedVolume[bVolNum].fReadOnly && (ulOpenMode != RED_O_RDONLY))\r
2392         {\r
2393             ret = -RED_EROFS;\r
2394         }\r
2395       #endif\r
2396         else\r
2397         {\r
2398             uint16_t    uHandleIdx;\r
2399             REDHANDLE  *pHandle = NULL;\r
2400 \r
2401             /*  Search for an unused handle.\r
2402             */\r
2403             for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)\r
2404             {\r
2405                 if(gaHandle[uHandleIdx].ulInode == INODE_INVALID)\r
2406                 {\r
2407                     pHandle = &gaHandle[uHandleIdx];\r
2408                     break;\r
2409                 }\r
2410             }\r
2411 \r
2412             /*  Error if all the handles are in use.\r
2413             */\r
2414             if(pHandle == NULL)\r
2415             {\r
2416                 ret = -RED_EMFILE;\r
2417             }\r
2418             else\r
2419             {\r
2420                 bool        fCreated = false;\r
2421                 uint16_t    uMode = 0U;\r
2422                 uint32_t    ulInode = 0U;       /* Init'd to quiet warnings. */\r
2423 \r
2424               #if REDCONF_VOLUME_COUNT > 1U\r
2425                 ret = RedCoreVolSetCurrent(bVolNum);\r
2426                 if(ret == 0)\r
2427               #endif\r
2428                 {\r
2429                   #if REDCONF_READ_ONLY == 0\r
2430                     if((ulOpenMode & RED_O_CREAT) != 0U)\r
2431                     {\r
2432                         uint32_t    ulPInode;\r
2433                         const char *pszName;\r
2434 \r
2435                         ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);\r
2436                         if(ret == 0)\r
2437                         {\r
2438                             ret = RedCoreCreate(ulPInode, pszName, false, &ulInode);\r
2439                             if(ret == 0)\r
2440                             {\r
2441                                 fCreated = true;\r
2442                             }\r
2443                             else if((ret == -RED_EEXIST) && ((ulOpenMode & RED_O_EXCL) == 0U))\r
2444                             {\r
2445                                 /*  If the path already exists and that's OK,\r
2446                                     lookup its inode number.\r
2447                                 */\r
2448                                 ret = RedCoreLookup(ulPInode, pszName, &ulInode);\r
2449                             }\r
2450                             else\r
2451                             {\r
2452                                 /*  No action, just propagate the error.\r
2453                                 */\r
2454                             }\r
2455                         }\r
2456                     }\r
2457                     else\r
2458                   #endif\r
2459                     {\r
2460                         ret = RedPathLookup(pszLocalPath, &ulInode);\r
2461                     }\r
2462                 }\r
2463 \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
2467                     inode on error.\r
2468                 */\r
2469                 if(!fCreated)\r
2470                 {\r
2471                     if(ret == 0)\r
2472                     {\r
2473                         REDSTAT s;\r
2474 \r
2475                         ret = RedCoreStat(ulInode, &s);\r
2476                         if(ret == 0)\r
2477                         {\r
2478                             uMode = s.st_mode;\r
2479                         }\r
2480                     }\r
2481 \r
2482                     /*  Error if the inode is not of the expected type.\r
2483                     */\r
2484                     if(ret == 0)\r
2485                     {\r
2486                         ret = ModeTypeCheck(uMode, type);\r
2487                     }\r
2488 \r
2489                     /*  Directories must always be opened with O_RDONLY.\r
2490                     */\r
2491                     if((ret == 0) && RED_S_ISDIR(uMode) && ((ulOpenMode & RED_O_RDONLY) == 0U))\r
2492                     {\r
2493                         ret = -RED_EISDIR;\r
2494                     }\r
2495 \r
2496                   #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FTRUNCATE == 1)\r
2497                     if((ret == 0) && ((ulOpenMode & RED_O_TRUNC) != 0U))\r
2498                     {\r
2499                         ret = RedCoreFileTruncate(ulInode, UINT64_SUFFIX(0));\r
2500                     }\r
2501                   #endif\r
2502                 }\r
2503 \r
2504                 if(ret == 0)\r
2505                 {\r
2506                     int32_t iFildes;\r
2507 \r
2508                     RedMemSet(pHandle, 0U, sizeof(*pHandle));\r
2509 \r
2510                     /*  Populate this handle, marking it as in use.\r
2511                     */\r
2512                     pHandle->ulInode = ulInode;\r
2513                     pHandle->bVolNum = bVolNum;\r
2514 \r
2515                     if(RED_S_ISDIR(uMode))\r
2516                     {\r
2517                         pHandle->bFlags |= HFLAG_DIRECTORY;\r
2518                     }\r
2519 \r
2520                     if(((ulOpenMode & RED_O_RDONLY) != 0U) || ((ulOpenMode & RED_O_RDWR) != 0U))\r
2521                     {\r
2522                         pHandle->bFlags |= HFLAG_READABLE;\r
2523                     }\r
2524 \r
2525                   #if REDCONF_READ_ONLY == 0\r
2526                     if(((ulOpenMode & RED_O_WRONLY) != 0U) || ((ulOpenMode & RED_O_RDWR) != 0U))\r
2527                     {\r
2528                         pHandle->bFlags |= HFLAG_WRITEABLE;\r
2529                     }\r
2530 \r
2531                     if((ulOpenMode & RED_O_APPEND) != 0U)\r
2532                     {\r
2533                         pHandle->bFlags |= HFLAG_APPENDING;\r
2534                     }\r
2535                   #endif\r
2536 \r
2537                     iFildes = FildesPack(uHandleIdx, bVolNum);\r
2538                     if(iFildes == -1)\r
2539                     {\r
2540                         /*  It should be impossible to get here, unless there\r
2541                             is memory corruption.\r
2542                         */\r
2543                         REDERROR();\r
2544                         ret = -RED_EFUBAR;\r
2545                     }\r
2546                     else\r
2547                     {\r
2548                         *piFildes = iFildes;\r
2549                     }\r
2550                 }\r
2551             }\r
2552         }\r
2553     }\r
2554 \r
2555     return ret;\r
2556 }\r
2557 \r
2558 \r
2559 /** @brief Close a file descriptor.\r
2560 \r
2561     @param iFildes  The file descriptor to close.\r
2562 \r
2563     @return A negated ::REDSTATUS code indicating the operation result.\r
2564 \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
2568 */\r
2569 static REDSTATUS FildesClose(\r
2570     int32_t     iFildes)\r
2571 {\r
2572     REDHANDLE  *pHandle;\r
2573     REDSTATUS   ret;\r
2574 \r
2575     ret = FildesToHandle(iFildes, FTYPE_EITHER, &pHandle);\r
2576 \r
2577   #if REDCONF_READ_ONLY == 0\r
2578   #if REDCONF_VOLUME_COUNT > 1U\r
2579     if(ret == 0)\r
2580     {\r
2581         ret = RedCoreVolSetCurrent(pHandle->bVolNum);\r
2582     }\r
2583   #endif\r
2584 \r
2585     /*  No core event for close, so this transaction flag needs to be\r
2586         implemented here.\r
2587     */\r
2588     if(ret == 0)\r
2589     {\r
2590         uint32_t    ulTransMask;\r
2591 \r
2592         ret = RedCoreTransMaskGet(&ulTransMask);\r
2593 \r
2594         if((ret == 0) && ((ulTransMask & RED_TRANSACT_CLOSE) != 0U))\r
2595         {\r
2596             ret = RedCoreVolTransact();\r
2597         }\r
2598     }\r
2599   #endif\r
2600 \r
2601     if(ret == 0)\r
2602     {\r
2603         /*  Mark this handle as unused.\r
2604         */\r
2605         pHandle->ulInode = INODE_INVALID;\r
2606     }\r
2607 \r
2608     return ret;\r
2609 }\r
2610 \r
2611 \r
2612 /** @brief Convert a file descriptor into a handle pointer.\r
2613 \r
2614     Also validates the file descriptor.\r
2615 \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
2621 \r
2622     @return A negated ::REDSTATUS code indicating the operation result.\r
2623 \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
2628                             directory.\r
2629     @retval -RED_ENOTDIR    Expected a directory, but the file descriptor is for\r
2630                             a file.\r
2631 */\r
2632 static REDSTATUS FildesToHandle(\r
2633     int32_t     iFildes,\r
2634     FTYPE       expectedType,\r
2635     REDHANDLE **ppHandle)\r
2636 {\r
2637     REDSTATUS   ret;\r
2638 \r
2639     if(ppHandle == NULL)\r
2640     {\r
2641         REDERROR();\r
2642         ret = -RED_EINVAL;\r
2643     }\r
2644     else if(iFildes < FD_MIN)\r
2645     {\r
2646         ret = -RED_EBADF;\r
2647     }\r
2648     else\r
2649     {\r
2650         uint16_t uHandleIdx;\r
2651         uint8_t  bVolNum;\r
2652         uint16_t uGeneration;\r
2653 \r
2654         FildesUnpack(iFildes, &uHandleIdx, &bVolNum, &uGeneration);\r
2655 \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
2661         {\r
2662             ret = -RED_EBADF;\r
2663         }\r
2664         else if((expectedType == FTYPE_FILE) && ((gaHandle[uHandleIdx].bFlags & HFLAG_DIRECTORY) != 0U))\r
2665         {\r
2666             ret = -RED_EISDIR;\r
2667         }\r
2668         else if((expectedType == FTYPE_DIR) && ((gaHandle[uHandleIdx].bFlags & HFLAG_DIRECTORY) == 0U))\r
2669         {\r
2670             ret = -RED_ENOTDIR;\r
2671         }\r
2672         else\r
2673         {\r
2674             *ppHandle = &gaHandle[uHandleIdx];\r
2675             ret = 0;\r
2676         }\r
2677     }\r
2678 \r
2679     return ret;\r
2680 }\r
2681 \r
2682 \r
2683 /** @brief Pack a file descriptor.\r
2684 \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
2689 \r
2690     @return The packed file descriptor.\r
2691 */\r
2692 static int32_t FildesPack(\r
2693     uint16_t    uHandleIdx,\r
2694     uint8_t     bVolNum)\r
2695 {\r
2696     int32_t     iFildes;\r
2697 \r
2698     if((uHandleIdx >= REDCONF_HANDLE_COUNT) || (bVolNum >= REDCONF_VOLUME_COUNT))\r
2699     {\r
2700         REDERROR();\r
2701         iFildes = -1;\r
2702     }\r
2703     else\r
2704     {\r
2705         uint32_t    ulFdBits;\r
2706 \r
2707         REDASSERT(gauGeneration[bVolNum] <= FD_GEN_MAX);\r
2708         REDASSERT(gauGeneration[bVolNum] != 0U);\r
2709 \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
2715 \r
2716         iFildes = (int32_t)ulFdBits;\r
2717 \r
2718         if(iFildes < FD_MIN)\r
2719         {\r
2720             REDERROR();\r
2721             iFildes = -1;\r
2722         }\r
2723     }\r
2724 \r
2725     return iFildes;\r
2726 }\r
2727 \r
2728 \r
2729 /** @brief Unpack a file descriptor.\r
2730 \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
2738 */\r
2739 static void FildesUnpack(\r
2740     int32_t     iFildes,\r
2741     uint16_t   *puHandleIdx,\r
2742     uint8_t    *pbVolNum,\r
2743     uint16_t   *puGeneration)\r
2744 {\r
2745     uint32_t ulFdBits = (uint32_t)iFildes;\r
2746 \r
2747     REDASSERT(iFildes >= FD_MIN);\r
2748 \r
2749     if(puHandleIdx != NULL)\r
2750     {\r
2751         *puHandleIdx = (uint16_t)(ulFdBits & FD_IDX_MAX);\r
2752     }\r
2753 \r
2754     ulFdBits >>= FD_IDX_BITS;\r
2755 \r
2756     if(pbVolNum != NULL)\r
2757     {\r
2758         *pbVolNum = (uint8_t)(ulFdBits & FD_VOL_MAX);\r
2759     }\r
2760 \r
2761     ulFdBits >>= FD_VOL_BITS;\r
2762 \r
2763     if(puGeneration != NULL)\r
2764     {\r
2765         *puGeneration = (uint16_t)(ulFdBits & FD_GEN_MAX);\r
2766     }\r
2767 }\r
2768 \r
2769 \r
2770 #if REDCONF_API_POSIX_READDIR == 1\r
2771 /** @brief Validate a directory stream object.\r
2772 \r
2773     @param pDirStream   The directory stream to validate.\r
2774 \r
2775     @return Whether the directory stream is valid.\r
2776 \r
2777     @retval true    The directory stream object appears valid.\r
2778     @retval false   The directory stream object is invalid.\r
2779 */\r
2780 static bool DirStreamIsValid(\r
2781     const REDDIR   *pDirStream)\r
2782 {\r
2783     bool            fRet = true;\r
2784 \r
2785     if(pDirStream == NULL)\r
2786     {\r
2787         fRet = false;\r
2788     }\r
2789     else\r
2790     {\r
2791         uint16_t uHandleIdx;\r
2792 \r
2793         /*  pDirStream should be a pointer to one of the handles.\r
2794 \r
2795             A good compiler will optimize this loop into a bounds check and an\r
2796             alignment check.\r
2797         */\r
2798         for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)\r
2799         {\r
2800             if(pDirStream == &gaHandle[uHandleIdx])\r
2801             {\r
2802                 break;\r
2803             }\r
2804         }\r
2805 \r
2806         if(uHandleIdx < REDCONF_HANDLE_COUNT)\r
2807         {\r
2808             /*  The handle must be in use, have a valid volume number, and be a\r
2809                 directory handle.\r
2810             */\r
2811             if(    (pDirStream->ulInode == INODE_INVALID)\r
2812                 || (pDirStream->bVolNum >= REDCONF_VOLUME_COUNT)\r
2813                 || ((pDirStream->bFlags & HFLAG_DIRECTORY) == 0U))\r
2814             {\r
2815                 fRet = false;\r
2816             }\r
2817         }\r
2818         else\r
2819         {\r
2820             /*  pDirStream is a non-null pointer, but it is not a pointer to one\r
2821                 of our handles.\r
2822             */\r
2823             fRet = false;\r
2824         }\r
2825     }\r
2826 \r
2827     return fRet;\r
2828 }\r
2829 #endif\r
2830 \r
2831 \r
2832 /** @brief Enter the file system driver.\r
2833 \r
2834     @return A negated ::REDSTATUS code indicating the operation result.\r
2835 \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
2839 */\r
2840 static REDSTATUS PosixEnter(void)\r
2841 {\r
2842     REDSTATUS ret;\r
2843 \r
2844     if(gfPosixInited)\r
2845     {\r
2846       #if REDCONF_TASK_COUNT > 1U\r
2847         RedOsMutexAcquire();\r
2848 \r
2849         ret = TaskRegister(NULL);\r
2850         if(ret != 0)\r
2851         {\r
2852             RedOsMutexRelease();\r
2853         }\r
2854       #else\r
2855         ret = 0;\r
2856       #endif\r
2857     }\r
2858     else\r
2859     {\r
2860         ret = -RED_EINVAL;\r
2861     }\r
2862 \r
2863     return ret;\r
2864 }\r
2865 \r
2866 \r
2867 /** @brief Leave the file system driver.\r
2868 */\r
2869 static void PosixLeave(void)\r
2870 {\r
2871     /*  If the driver was uninitialized, PosixEnter() should have failed and we\r
2872         should not be calling PosixLeave().\r
2873     */\r
2874     REDASSERT(gfPosixInited);\r
2875 \r
2876   #if REDCONF_TASK_COUNT > 1U\r
2877     RedOsMutexRelease();\r
2878   #endif\r
2879 }\r
2880 \r
2881 \r
2882 /** @brief Check that a mode is consistent with the given expected type.\r
2883 \r
2884     @param uMode        An inode mode, indicating whether the inode is a file\r
2885                         or a directory.\r
2886     @param expectedType The expected type: ::FTYPE_FILE, ::FTYPE_DIR, or\r
2887                         ::FTYPE_EITHER.\r
2888 \r
2889     @return A negated ::REDSTATUS code indicating the operation result.\r
2890 \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
2894 */\r
2895 static REDSTATUS ModeTypeCheck(\r
2896     uint16_t    uMode,\r
2897     FTYPE       expectedType)\r
2898 {\r
2899     REDSTATUS   ret;\r
2900 \r
2901     if((expectedType == FTYPE_FILE) && RED_S_ISDIR(uMode))\r
2902     {\r
2903         /*  Expected file, found directory.\r
2904         */\r
2905         ret = -RED_EISDIR;\r
2906     }\r
2907     else if((expectedType == FTYPE_DIR) && RED_S_ISREG(uMode))\r
2908     {\r
2909         /*  Expected directory, found file.\r
2910         */\r
2911         ret = -RED_ENOTDIR;\r
2912     }\r
2913     else\r
2914     {\r
2915         /*  No expected type or found what we expected.\r
2916         */\r
2917         ret = 0;\r
2918     }\r
2919 \r
2920     return ret;\r
2921 }\r
2922 \r
2923 \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
2926 \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
2930 \r
2931     @param ulInode  The inode whose name is to be unlinked.\r
2932 \r
2933     @return A negated ::REDSTATUS code indicating the operation result.\r
2934 \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
2939 */\r
2940 static REDSTATUS InodeUnlinkCheck(\r
2941     uint32_t    ulInode)\r
2942 {\r
2943     uint16_t    uHandleIdx;\r
2944     REDSTATUS   ret;\r
2945 \r
2946   #if REDCONF_API_POSIX_LINK == 0\r
2947     ret = 0;\r
2948   #else\r
2949     REDSTAT     InodeStat;\r
2950 \r
2951     ret = RedCoreStat(ulInode, &InodeStat);\r
2952 \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
2956     */\r
2957     if((ret == 0) && (InodeStat.st_nlink == 1U))\r
2958   #endif\r
2959     {\r
2960         for(uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++)\r
2961         {\r
2962             if((gaHandle[uHandleIdx].ulInode == ulInode) && (gaHandle[uHandleIdx].bVolNum == gbRedVolNum))\r
2963             {\r
2964                 ret = -RED_EBUSY;\r
2965                 break;\r
2966             }\r
2967         }\r
2968     }\r
2969 \r
2970     return ret;\r
2971 }\r
2972 #endif\r
2973 \r
2974 \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
2978 \r
2979     The caller must hold the FS mutex.\r
2980 \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
2984                         been registered.\r
2985 \r
2986     @return A negated ::REDSTATUS code indicating the operation result.\r
2987 \r
2988     @retval 0           Operation was successful.\r
2989     @retval -RED_EUSERS Cannot become a file system user: too many users.\r
2990 */\r
2991 static REDSTATUS TaskRegister(\r
2992     uint32_t   *pulTaskIdx)\r
2993 {\r
2994     uint32_t    ulTaskId = RedOsTaskId();\r
2995     uint32_t    ulFirstFreeIdx = REDCONF_TASK_COUNT;\r
2996     uint32_t    ulIdx;\r
2997     REDSTATUS   ret;\r
2998 \r
2999     REDASSERT(ulTaskId != 0U);\r
3000 \r
3001     /*  Scan the task slots to determine if the task is registered as a file\r
3002         system task.\r
3003     */\r
3004     for(ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++)\r
3005     {\r
3006         if(gaTask[ulIdx].ulTaskId == ulTaskId)\r
3007         {\r
3008             break;\r
3009         }\r
3010 \r
3011         if((ulFirstFreeIdx == REDCONF_TASK_COUNT) && (gaTask[ulIdx].ulTaskId == 0U))\r
3012         {\r
3013             ulFirstFreeIdx = ulIdx;\r
3014         }\r
3015     }\r
3016 \r
3017     if(ulIdx == REDCONF_TASK_COUNT)\r
3018     {\r
3019         /*  Task not already registered.\r
3020         */\r
3021         if(ulFirstFreeIdx == REDCONF_TASK_COUNT)\r
3022         {\r
3023             /*  Cannot register task, no more slots.\r
3024             */\r
3025             ret = -RED_EUSERS;\r
3026         }\r
3027         else\r
3028         {\r
3029             /*  Registering task.\r
3030             */\r
3031             ulIdx = ulFirstFreeIdx;\r
3032             gaTask[ulIdx].ulTaskId = ulTaskId;\r
3033             ret = 0;\r
3034         }\r
3035     }\r
3036     else\r
3037     {\r
3038         /*  Task already registered.\r
3039         */\r
3040         ret = 0;\r
3041     }\r
3042 \r
3043     if((ret == 0) && (pulTaskIdx != NULL))\r
3044     {\r
3045         *pulTaskIdx = ulIdx;\r
3046     }\r
3047 \r
3048     return ret;\r
3049 }\r
3050 #endif /* REDCONF_TASK_COUNT > 1U */\r
3051 \r
3052 \r
3053 /** @brief Convert an error value into a simple 0 or -1 return.\r
3054 \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
3059 \r
3060     @param  iError  The error value.\r
3061 \r
3062     @return Returns 0 if @p iError is 0; otherwise, returns -1.\r
3063 */\r
3064 static int32_t PosixReturn(\r
3065     REDSTATUS   iError)\r
3066 {\r
3067     int32_t     iReturn;\r
3068 \r
3069     if(iError == 0)\r
3070     {\r
3071         iReturn = 0;\r
3072     }\r
3073     else\r
3074     {\r
3075         iReturn = -1;\r
3076 \r
3077         /*  The errors should be negative, and errno positive.\r
3078         */\r
3079         REDASSERT(iError < 0);\r
3080         red_errno = -iError;\r
3081     }\r
3082 \r
3083     return iReturn;\r
3084 }\r
3085 \r
3086 \r
3087 #endif /* REDCONF_API_POSIX == 1 */\r
3088 \r