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