1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
\r
3 Copyright (c) 2014-2015 Datalight, Inc.
\r
4 All Rights Reserved Worldwide.
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; use version 2 of the License.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
\r
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
19 /* Businesses and individuals that for commercial or other reasons cannot
\r
20 comply with the terms of the GPLv2 license may obtain a commercial license
\r
21 before incorporating Reliance Edge into proprietary software for
\r
22 distribution in any form. Visit http://www.datalight.com/reliance-edge for
\r
26 @brief Implements the entry-points to the core file system.
\r
29 #include <redcoreapi.h>
\r
30 #include <redcore.h>
\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
37 #define MINIMUM_METADATA_BLOCKS (5U)
\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
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
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
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
52 #if REDCONF_READ_ONLY == 0
\r
53 static REDSTATUS CoreFileWrite(uint32_t ulInode, uint64_t ullStart, uint32_t *pulLen, const void *pBuffer);
\r
55 #if TRUNCATE_SUPPORTED
\r
56 static REDSTATUS CoreFileTruncate(uint32_t ulInode, uint64_t ullSize);
\r
60 VOLUME gaRedVolume[REDCONF_VOLUME_COUNT];
\r
61 static COREVOLUME gaCoreVol[REDCONF_VOLUME_COUNT];
\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
68 CONST_IF_ONE_VOLUME uint8_t gbRedVolNum;
\r
71 /** @brief Initialize the Reliance Edge file system driver.
\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
77 If this function is called when the Reliance Edge driver is already
\r
78 initialized, the behavior is undefined.
\r
80 @return A negated ::REDSTATUS code indicating the operation result.
\r
82 @retval 0 Operation was successful.
\r
84 REDSTATUS RedCoreInit(void)
\r
88 #if REDCONF_OUTPUT == 1
\r
89 static uint8_t bSignedOn = 0U; /* Whether the sign on has been printed. */
\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
103 RedMemSet(gaRedVolume, 0U, sizeof(gaRedVolume));
\r
104 RedMemSet(gaCoreVol, 0U, sizeof(gaCoreVol));
\r
108 for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)
\r
110 VOLUME *pVol = &gaRedVolume[bVolNum];
\r
111 COREVOLUME *pCoreVol = &gaCoreVol[bVolNum];
\r
112 const VOLCONF *pVolConf = &gaRedVolConf[bVolNum];
\r
114 if( (pVolConf->ulSectorSize < SECTOR_SIZE_MIN)
\r
115 || ((REDCONF_BLOCK_SIZE % pVolConf->ulSectorSize) != 0U)
\r
116 || (pVolConf->ulInodeCount == 0U))
\r
120 #if REDCONF_API_POSIX == 1
\r
121 else if(pVolConf->pszPathPrefix == NULL)
\r
127 #if REDCONF_VOLUME_COUNT > 1U
\r
130 /* Ensure there are no duplicate path prefixes. Check against all
\r
131 previous volumes, which are already verified.
\r
133 for(bCmpVol = 0U; bCmpVol < bVolNum; bCmpVol++)
\r
135 const char *pszCmpPathPrefix = gaRedVolConf[bCmpVol].pszPathPrefix;
\r
137 if(RedStrCmp(pVolConf->pszPathPrefix, pszCmpPathPrefix) == 0)
\r
149 pVol->bBlockSectorShift = 0U;
\r
150 while((pVolConf->ulSectorSize << pVol->bBlockSectorShift) < REDCONF_BLOCK_SIZE)
\r
152 pVol->bBlockSectorShift++;
\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
159 REDASSERT((pVolConf->ulSectorSize << pVol->bBlockSectorShift) == REDCONF_BLOCK_SIZE);
\r
161 pVol->ulBlockCount = (uint32_t)(pVolConf->ullSectorCount >> pVol->bBlockSectorShift);
\r
163 if(pVol->ulBlockCount < MINIMUM_METADATA_BLOCKS)
\r
169 #if REDCONF_READ_ONLY == 0
\r
170 pVol->ulTransMask = REDCONF_TRANSACT_DEFAULT;
\r
173 pVol->ullMaxInodeSize = INODE_SIZE_MAX;
\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
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
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
191 pCoreVol->fImapInline = (pVol->ulBlockCount - 3U) <= METAROOT_ENTRIES;
\r
193 if(pCoreVol->fImapInline)
\r
195 #if REDCONF_IMAP_INLINE == 1
\r
196 pCoreVol->ulInodeTableStartBN = 3U;
\r
203 #if REDCONF_IMAP_EXTERNAL == 1
\r
204 pCoreVol->ulImapStartBN = 3U;
\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
212 pCoreVol->ulImapNodeCount =
\r
213 ((pVol->ulBlockCount - 3U) + ((IMAPNODE_ENTRIES + 2U) - 1U)) / (IMAPNODE_ENTRIES + 2U);
\r
215 pCoreVol->ulInodeTableStartBN = pCoreVol->ulImapStartBN + (pCoreVol->ulImapNodeCount * 2U);
\r
225 pCoreVol->ulFirstAllocableBN = pCoreVol->ulInodeTableStartBN + (pVolConf->ulInodeCount * 2U);
\r
227 if(pCoreVol->ulFirstAllocableBN > pVol->ulBlockCount)
\r
229 /* We can get here if there is not enough space for the number
\r
230 of configured inodes.
\r
236 pVol->ulBlocksAllocable = pVol->ulBlockCount - pCoreVol->ulFirstAllocableBN;
\r
246 /* Make sure the configured endianness is correct.
\r
250 uint16_t uValue = 0xFF00U;
\r
251 uint8_t abBytes[2U];
\r
253 RedMemCpy(abBytes, &uValue, sizeof(abBytes));
\r
255 #if REDCONF_ENDIAN_BIG == 1
\r
256 if(abBytes[0U] != 0xFFU)
\r
258 if(abBytes[0U] != 0x00U)
\r
267 ret = RedOsClockInit();
\r
269 #if REDCONF_TASK_COUNT > 1U
\r
272 ret = RedOsMutexInit();
\r
276 (void)RedOsClockUninit();
\r
286 /** @brief Uninitialize the Reliance Edge file system driver.
\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
292 The behavior of calling this function when the core is already uninitialized
\r
295 @return A negated ::REDSTATUS code indicating the operation result.
\r
297 @retval 0 Operation was successful.
\r
298 @retval -RED_EBUSY At least one volume is still mounted.
\r
300 REDSTATUS RedCoreUninit(void)
\r
304 #if REDCONF_TASK_COUNT > 1U
\r
305 ret = RedOsMutexUninit();
\r
310 ret = RedOsClockUninit();
\r
317 /** @brief Set the current volume.
\r
319 All core APIs operate on the current volume. This call must precede all
\r
322 @param bVolNum The volume number to access.
\r
324 @return A negated ::REDSTATUS code indicating the operation result.
\r
326 @retval 0 Operation was successful.
\r
327 @retval -RED_EINVAL @p bVolNum is an invalid volume number.
\r
329 REDSTATUS RedCoreVolSetCurrent(
\r
334 if(bVolNum >= REDCONF_VOLUME_COUNT)
\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
355 #if FORMAT_SUPPORTED
\r
356 /** @brief Format a file system volume.
\r
358 Uses the statically defined volume configuration. After calling this
\r
359 function, the volume needs to be mounted -- see RedCoreVolMount().
\r
361 An error is returned if the volume is mounted.
\r
363 @return A negated ::REDSTATUS code indicating the operation result.
\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
369 REDSTATUS RedCoreVolFormat(void)
\r
371 return RedVolFormat();
\r
373 #endif /* FORMAT_SUPPORTED */
\r
376 /** @brief Mount a file system volume.
\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
382 If the volume is already mounted, the behavior is undefined.
\r
384 @return A negated ::REDSTATUS code indicating the operation result.
\r
386 @retval 0 Operation was successful.
\r
387 @retval -RED_EIO Volume not formatted, improperly formatted, or corrupt.
\r
389 REDSTATUS RedCoreVolMount(void)
\r
391 return RedVolMount();
\r
395 /** @brief Unmount a file system volume.
\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
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
406 If the volume is already unmounted, the behavior is undefined.
\r
408 @return A negated ::REDSTATUS code indicating the operation result.
\r
410 @retval 0 Operation was successful.
\r
411 @retval -RED_EIO I/O error during unmount automatic transaction point.
\r
413 REDSTATUS RedCoreVolUnmount(void)
\r
417 #if REDCONF_READ_ONLY == 0
\r
418 if(!gpRedVolume->fReadOnly && ((gpRedVolume->ulTransMask & RED_TRANSACT_UMOUNT) != 0U))
\r
420 ret = RedVolTransact();
\r
426 ret = RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount);
\r
431 ret = RedOsBDevClose(gbRedVolNum);
\r
436 gpRedVolume->fMounted = false;
\r
443 #if REDCONF_READ_ONLY == 0
\r
444 /** @brief Commit a transaction point.
\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
454 @return A negated ::REDSTATUS code indicating the operation result.
\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
461 REDSTATUS RedCoreVolTransact(void)
\r
465 if(!gpRedVolume->fMounted)
\r
469 else if(gpRedVolume->fReadOnly)
\r
475 ret = RedVolTransact();
\r
480 #endif /* REDCONF_READ_ONLY == 0 */
\r
483 #if REDCONF_API_POSIX == 1
\r
484 /** @brief Query file system status information.
\r
486 @param pStatFS The buffer to populate with volume information.
\r
488 @return A negated ::REDSTATUS code indicating the operation result.
\r
490 @retval -RED_EINVAL Volume is not mounted; or @p pStatFS is `NULL`.
\r
492 REDSTATUS RedCoreVolStat(
\r
493 REDSTATFS *pStatFS)
\r
497 if((pStatFS == NULL) || (!gpRedVolume->fMounted))
\r
503 RedMemSet(pStatFS, 0U, sizeof(*pStatFS));
\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
511 pStatFS->f_bfree = gpRedMR->ulFreeBlocks;
\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
518 pStatFS->f_flag = RED_ST_NOSUID;
\r
519 #if REDCONF_READ_ONLY == 0
\r
520 if(gpRedVolume->fReadOnly)
\r
523 pStatFS->f_flag |= RED_ST_RDONLY;
\r
526 pStatFS->f_namemax = REDCONF_NAME_MAX;
\r
527 pStatFS->f_maxfsize = INODE_SIZE_MAX;
\r
528 pStatFS->f_dev = gbRedVolNum;
\r
535 #endif /* REDCONF_API_POSIX == 1 */
\r
538 #if (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX == 1) || (REDCONF_API_FSE_TRANSMASKSET == 1))
\r
539 /** @brief Update the transaction mask.
\r
541 The following events are available when using the FSE API:
\r
543 - #RED_TRANSACT_UMOUNT
\r
544 - #RED_TRANSACT_WRITE
\r
545 - #RED_TRANSACT_TRUNCATE
\r
546 - #RED_TRANSACT_VOLFULL
\r
548 The following events are available when using the POSIX-like API:
\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
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
566 Attempting to enable events for excluded functionality will result in an
\r
569 @param ulEventMask A bitwise-OR'd mask of automatic transaction events to
\r
570 be set as the current transaction mode.
\r
572 @return A negated ::REDSTATUS code indicating the operation result.
\r
574 @retval 0 Operation was successful.
\r
575 @retval -RED_EINVAL The volume is not mounted; or @p ulEventMask contains
\r
577 @retval -RED_EROFS The file system volume is read-only.
\r
579 REDSTATUS RedCoreTransMaskSet(
\r
580 uint32_t ulEventMask)
\r
584 if(!gpRedVolume->fMounted || ((ulEventMask & RED_TRANSACT_MASK) != ulEventMask))
\r
588 else if(gpRedVolume->fReadOnly)
\r
594 gpRedVolume->ulTransMask = ulEventMask;
\r
603 #if (REDCONF_API_POSIX == 1) || (REDCONF_API_FSE_TRANSMASKGET == 1)
\r
604 /** @brief Read the transaction mask.
\r
606 If the volume is read-only, the returned event mask is always zero.
\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
612 @return A negated ::REDSTATUS code indicating the operation result.
\r
614 @retval 0 Operation was successful.
\r
615 @retval -RED_EINVAL The volume is not mounted; or @p pulEventMask is `NULL`.
\r
617 REDSTATUS RedCoreTransMaskGet(
\r
618 uint32_t *pulEventMask)
\r
622 if(!gpRedVolume->fMounted || (pulEventMask == NULL))
\r
628 #if REDCONF_READ_ONLY == 1
\r
629 *pulEventMask = 0U;
\r
631 *pulEventMask = gpRedVolume->ulTransMask;
\r
641 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1)
\r
642 /** @brief Create a file or directory.
\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
650 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
666 REDSTATUS RedCoreCreate(
\r
668 const char *pszName,
\r
670 uint32_t *pulInode)
\r
674 if(!gpRedVolume->fMounted)
\r
678 else if(gpRedVolume->fReadOnly)
\r
684 ret = CoreCreate(ulPInode, pszName, fDir, pulInode);
\r
686 if( (ret == -RED_ENOSPC)
\r
687 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
688 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
690 ret = RedVolTransact();
\r
694 ret = CoreCreate(ulPInode, pszName, fDir, pulInode);
\r
700 if(fDir && ((gpRedVolume->ulTransMask & RED_TRANSACT_MKDIR) != 0U))
\r
702 ret = RedVolTransact();
\r
704 else if(!fDir && ((gpRedVolume->ulTransMask & RED_TRANSACT_CREAT) != 0U))
\r
706 ret = RedVolTransact();
\r
710 /* No automatic transaction for this operation.
\r
720 /** @brief Create a file or directory.
\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
728 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
742 static REDSTATUS CoreCreate(
\r
744 const char *pszName,
\r
746 uint32_t *pulInode)
\r
750 if(pulInode == NULL)
\r
754 else if(gpRedVolume->fReadOnly)
\r
762 pino.ulInode = ulPInode;
\r
763 ret = RedInodeMount(&pino, FTYPE_DIR, false);
\r
769 ino.ulInode = INODE_INVALID;
\r
770 ret = RedInodeCreate(&ino, ulPInode, fDir ? RED_S_IFDIR : RED_S_IFREG);
\r
774 ret = RedInodeBranch(&pino);
\r
778 ret = RedDirEntryCreate(&pino, pszName, ino.ulInode);
\r
783 *pulInode = ino.ulInode;
\r
789 ret2 = RedInodeFree(&ino);
\r
790 CRITICAL_ASSERT(ret2 == 0);
\r
793 RedInodePut(&ino, 0U);
\r
796 RedInodePut(&pino, (ret == 0) ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
802 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) */
\r
805 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)
\r
806 /** @brief Create a hard link.
\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
815 If @p ulInode names a directory, the operation will fail.
\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
821 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
841 REDSTATUS RedCoreLink(
\r
843 const char *pszName,
\r
848 if(!gpRedVolume->fMounted)
\r
852 else if(gpRedVolume->fReadOnly)
\r
858 ret = CoreLink(ulPInode, pszName, ulInode);
\r
860 if( (ret == -RED_ENOSPC)
\r
861 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
862 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
864 ret = RedVolTransact();
\r
868 ret = CoreLink(ulPInode, pszName, ulInode);
\r
872 if((ret == 0) && ((gpRedVolume->ulTransMask & RED_TRANSACT_LINK) != 0U))
\r
874 ret = RedVolTransact();
\r
882 /** @brief Create a hard link.
\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
888 @return A negated ::REDSTATUS code indicating the operation result.
\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
906 static REDSTATUS CoreLink(
\r
908 const char *pszName,
\r
913 if(gpRedVolume->fReadOnly)
\r
921 pino.ulInode = ulPInode;
\r
922 ret = RedInodeMount(&pino, FTYPE_DIR, false);
\r
928 ino.ulInode = ulInode;
\r
929 ret = RedInodeMount(&ino, FTYPE_FILE, false);
\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
935 if(ret == -RED_EISDIR)
\r
942 if(ino.pInodeBuf->uNLink == UINT16_MAX)
\r
948 ret = RedInodeBranch(&pino);
\r
953 ret = RedInodeBranch(&ino);
\r
958 ret = RedDirEntryCreate(&pino, pszName, ino.ulInode);
\r
963 ino.pInodeBuf->uNLink++;
\r
966 RedInodePut(&ino, (ret == 0) ? IPUT_UPDATE_CTIME : 0U);
\r
969 RedInodePut(&pino, (ret == 0) ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
975 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1) */
\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
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
985 If the path names a directory which is not empty, the unlink will fail.
\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
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
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
1001 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
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
1015 @retval -RED_ENOTEMPTY The inode refered to by @p pszName is a
\r
1016 directory which is not empty.
\r
1018 REDSTATUS RedCoreUnlink(
\r
1019 uint32_t ulPInode,
\r
1020 const char *pszName)
\r
1024 if(!gpRedVolume->fMounted)
\r
1026 ret = -RED_EINVAL;
\r
1028 else if(gpRedVolume->fReadOnly)
\r
1034 ret = CoreUnlink(ulPInode, pszName);
\r
1036 if( (ret == -RED_ENOSPC)
\r
1037 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
1038 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
1040 ret = RedVolTransact();
\r
1044 ret = CoreUnlink(ulPInode, pszName);
\r
1048 if((ret == 0) && ((gpRedVolume->ulTransMask & RED_TRANSACT_UNLINK) != 0U))
\r
1050 ret = RedVolTransact();
\r
1058 /** @brief Delete a file or directory.
\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
1064 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
1076 @retval -RED_ENOTEMPTY The inode refered to by @p pszName is a
\r
1077 directory which is not empty.
\r
1079 static REDSTATUS CoreUnlink(
\r
1080 uint32_t ulPInode,
\r
1081 const char *pszName)
\r
1085 if(gpRedVolume->fReadOnly)
\r
1093 pino.ulInode = ulPInode;
\r
1094 ret = RedInodeMount(&pino, FTYPE_DIR, false);
\r
1098 uint32_t ulDeleteIdx;
\r
1101 ret = RedDirEntryLookup(&pino, pszName, &ulDeleteIdx, &ulInode);
\r
1105 ret = RedInodeBranch(&pino);
\r
1112 ino.ulInode = ulInode;
\r
1113 ret = RedInodeMount(&ino, FTYPE_EITHER, false);
\r
1117 if(ino.fDirectory && (ino.pInodeBuf->ullSize > 0U))
\r
1119 ret = -RED_ENOTEMPTY;
\r
1123 #if RESERVED_BLOCKS > 0U
\r
1124 gpRedCoreVol->fUseReservedBlocks = true;
\r
1127 ret = RedDirEntryDelete(&pino, ulDeleteIdx);
\r
1129 #if RESERVED_BLOCKS > 0U
\r
1130 gpRedCoreVol->fUseReservedBlocks = false;
\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
1142 RedInodePutCoord(&pino);
\r
1144 ret = RedInodeLinkDec(&ino);
\r
1145 CRITICAL_ASSERT(ret == 0);
\r
1149 RedInodePut(&ino, (ret == 0) ? IPUT_UPDATE_CTIME : 0U);
\r
1153 RedInodePut(&pino, (ret == 0) ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
1159 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1)) */
\r
1162 #if REDCONF_API_POSIX == 1
\r
1163 /** @brief Look up the inode number of a file or directory.
\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
1168 @param pulInode On successful return, populated with the inode number named
\r
1171 @return A negated ::REDSTATUS code indicating the operation result.
\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
1181 REDSTATUS RedCoreLookup(
\r
1182 uint32_t ulPInode,
\r
1183 const char *pszName,
\r
1184 uint32_t *pulInode)
\r
1188 if((pulInode == NULL) || !gpRedVolume->fMounted)
\r
1190 ret = -RED_EINVAL;
\r
1196 ino.ulInode = ulPInode;
\r
1197 ret = RedInodeMount(&ino, FTYPE_DIR, false);
\r
1201 ret = RedDirEntryLookup(&ino, pszName, NULL, pulInode);
\r
1203 RedInodePut(&ino, 0U);
\r
1209 #endif /* REDCONF_API_POSIX == 1 */
\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
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
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
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
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 pszNewPath The new name of the file or directory after the rename.
\r
1240 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
1261 @retval -RED_ENOTEMPTY The destination name is a directory which is not
\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
1268 REDSTATUS RedCoreRename(
\r
1269 uint32_t ulSrcPInode,
\r
1270 const char *pszSrcName,
\r
1271 uint32_t ulDstPInode,
\r
1272 const char *pszDstName)
\r
1276 if(!gpRedVolume->fMounted)
\r
1278 ret = -RED_EINVAL;
\r
1280 else if(gpRedVolume->fReadOnly)
\r
1286 ret = CoreRename(ulSrcPInode, pszSrcName, ulDstPInode, pszDstName);
\r
1288 if( (ret == -RED_ENOSPC)
\r
1289 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
1290 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
1292 ret = RedVolTransact();
\r
1296 ret = CoreRename(ulSrcPInode, pszSrcName, ulDstPInode, pszDstName);
\r
1300 if((ret == 0) && ((gpRedVolume->ulTransMask & RED_TRANSACT_RENAME) != 0U))
\r
1302 ret = RedVolTransact();
\r
1310 /** @brief Rename a file or directory.
\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 pszNewPath The new name of the file or directory after the rename.
\r
1319 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
1338 @retval -RED_ENOTEMPTY The destination name is a directory which is not
\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
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
1353 if(gpRedVolume->fReadOnly)
\r
1359 bool fUpdateTimestamps = false;
\r
1362 SrcPInode.ulInode = ulSrcPInode;
\r
1363 ret = RedInodeMount(&SrcPInode, FTYPE_DIR, true);
\r
1368 CINODE *pDstPInode;
\r
1370 if(ulSrcPInode == ulDstPInode)
\r
1372 pDstPInode = &SrcPInode;
\r
1376 pDstPInode = &DstPInode;
\r
1377 DstPInode.ulInode = ulDstPInode;
\r
1378 ret = RedInodeMount(pDstPInode, FTYPE_DIR, true);
\r
1383 /* Initialize these to zero so we can unconditionally put them,
\r
1384 even if RedDirEntryRename() fails before mounting them.
\r
1386 CINODE SrcInode = {0U};
\r
1387 CINODE DstInode = {0U};
\r
1389 ret = RedDirEntryRename(&SrcPInode, pszSrcName, &SrcInode, pDstPInode, pszDstName, &DstInode);
\r
1391 #if REDCONF_RENAME_ATOMIC == 1
\r
1392 if((ret == 0) && (DstInode.ulInode != INODE_INVALID) && (DstInode.ulInode != SrcInode.ulInode))
\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
1400 RedInodePutCoord(&SrcPInode);
\r
1401 RedInodePutCoord(pDstPInode);
\r
1403 ret = RedInodeLinkDec(&DstInode);
\r
1404 CRITICAL_ASSERT(ret == 0);
\r
1407 if((ret == 0) && (DstInode.ulInode != SrcInode.ulInode))
\r
1412 fUpdateTimestamps = true;
\r
1415 #if REDCONF_RENAME_ATOMIC == 1
\r
1416 RedInodePut(&DstInode, 0U);
\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
1423 RedInodePut(&SrcInode, fUpdateTimestamps ? IPUT_UPDATE_CTIME : 0U);
\r
1424 RedInodePut(pDstPInode, fUpdateTimestamps ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
1428 RedInodePut(&SrcPInode, fUpdateTimestamps ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
1433 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_RENAME == 1) */
\r
1436 #if REDCONF_API_POSIX == 1
\r
1437 /** @brief Get the status of a file or directory.
\r
1439 See the ::REDSTAT type for the details of the information returned.
\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
1445 @return A negated ::REDSTATUS code indicating the operation result.
\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
1452 REDSTATUS RedCoreStat(
\r
1458 if(!gpRedVolume->fMounted || (pStat == NULL))
\r
1460 ret = -RED_EINVAL;
\r
1466 ino.ulInode = ulInode;
\r
1467 ret = RedInodeMount(&ino, FTYPE_EITHER, false);
\r
1470 RedMemSet(pStat, 0U, sizeof(*pStat));
\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
1478 pStat->st_nlink = 1U;
\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
1486 #if REDCONF_INODE_BLOCKS == 1
\r
1487 pStat->st_blocks = ino.pInodeBuf->ulBlocks;
\r
1490 RedInodePut(&ino, 0U);
\r
1496 #endif /* REDCONF_API_POSIX == 1 */
\r
1499 #if REDCONF_API_FSE == 1
\r
1500 /** @brief Get the size of a file.
\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
1505 @return A negated ::REDSTATUS code indicating the operation result.
\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
1513 REDSTATUS RedCoreFileSizeGet(
\r
1515 uint64_t *pullSize)
\r
1519 if(!gpRedVolume->fMounted || (pullSize == NULL))
\r
1521 ret = -RED_EINVAL;
\r
1527 ino.ulInode = ulInode;
\r
1528 ret = RedInodeMount(&ino, FTYPE_FILE, false);
\r
1531 *pullSize = ino.pInodeBuf->ullSize;
\r
1533 RedInodePut(&ino, 0U);
\r
1539 #endif /* REDCONF_API_FSE == 1 */
\r
1542 /** @brief Read from a file.
\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
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
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
1558 @param pBuffer The buffer to populate with the data read. Must be big
\r
1559 enough for the read request.
\r
1561 @return A negated ::REDSTATUS code indicating the operation result.
\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
1569 REDSTATUS RedCoreFileRead(
\r
1571 uint64_t ullStart,
\r
1577 if(!gpRedVolume->fMounted || (pulLen == NULL))
\r
1579 ret = -RED_EINVAL;
\r
1583 #if (REDCONF_ATIME == 1) && (REDCONF_READ_ONLY == 0)
\r
1584 bool fUpdateAtime = (*pulLen > 0U) && !gpRedVolume->fReadOnly;
\r
1586 bool fUpdateAtime = false;
\r
1590 ino.ulInode = ulInode;
\r
1591 ret = RedInodeMount(&ino, FTYPE_FILE, fUpdateAtime);
\r
1594 ret = RedInodeDataRead(&ino, ullStart, pulLen, pBuffer);
\r
1596 #if (REDCONF_ATIME == 1) && (REDCONF_READ_ONLY == 0)
\r
1597 RedInodePut(&ino, ((ret == 0) && fUpdateAtime) ? IPUT_UPDATE_ATIME : 0U);
\r
1599 RedInodePut(&ino, 0U);
\r
1608 #if REDCONF_READ_ONLY == 0
\r
1609 /** @brief Write to a file.
\r
1611 If the write extends beyond the end-of-file, the file size will be
\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
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
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
1631 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
1643 @retval -RED_EROFS The file system volume is read-only.
\r
1645 REDSTATUS RedCoreFileWrite(
\r
1647 uint64_t ullStart,
\r
1649 const void *pBuffer)
\r
1653 if(!gpRedVolume->fMounted)
\r
1655 ret = -RED_EINVAL;
\r
1657 else if(gpRedVolume->fReadOnly)
\r
1663 ret = CoreFileWrite(ulInode, ullStart, pulLen, pBuffer);
\r
1665 if( (ret == -RED_ENOSPC)
\r
1666 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
1667 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
1669 ret = RedVolTransact();
\r
1673 ret = CoreFileWrite(ulInode, ullStart, pulLen, pBuffer);
\r
1677 if((ret == 0) && ((gpRedVolume->ulTransMask & RED_TRANSACT_WRITE) != 0U))
\r
1679 ret = RedVolTransact();
\r
1687 /** @brief Write to a file.
\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
1696 @return A negated ::REDSTATUS code indicating the operation result.
\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
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
1707 @retval -RED_EROFS The file system volume is read-only.
\r
1709 static REDSTATUS CoreFileWrite(
\r
1711 uint64_t ullStart,
\r
1713 const void *pBuffer)
\r
1717 if(gpRedVolume->fReadOnly)
\r
1725 ino.ulInode = ulInode;
\r
1726 ret = RedInodeMount(&ino, FTYPE_FILE, true);
\r
1729 ret = RedInodeDataWrite(&ino, ullStart, pulLen, pBuffer);
\r
1731 RedInodePut(&ino, (ret == 0) ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
1737 #endif /* REDCONF_READ_ONLY == 0 */
\r
1740 #if TRUNCATE_SUPPORTED
\r
1741 /** @brief Set the file size.
\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
1749 @param ulInode The inode of the file to truncate.
\r
1750 @param ullSize The new file size, in bytes.
\r
1752 @return A negated ::REDSTATUS code indicating the operation result.
\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
1763 REDSTATUS RedCoreFileTruncate(
\r
1769 if(!gpRedVolume->fMounted)
\r
1771 ret = -RED_EINVAL;
\r
1773 else if(gpRedVolume->fReadOnly)
\r
1779 ret = CoreFileTruncate(ulInode, ullSize);
\r
1781 if( (ret == -RED_ENOSPC)
\r
1782 && ((gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL) != 0U)
\r
1783 && (gpRedCoreVol->ulAlmostFreeBlocks > 0U))
\r
1785 ret = RedVolTransact();
\r
1789 ret = CoreFileTruncate(ulInode, ullSize);
\r
1793 if((ret == 0) && ((gpRedVolume->ulTransMask & RED_TRANSACT_TRUNCATE) != 0U))
\r
1795 ret = RedVolTransact();
\r
1803 /** @brief Set the file size.
\r
1805 @param ulInode The inode of the file to truncate.
\r
1806 @param ullSize The new file size, in bytes.
\r
1808 @return A negated ::REDSTATUS code indicating the operation result.
\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
1818 static REDSTATUS CoreFileTruncate(
\r
1824 if(gpRedVolume->fReadOnly)
\r
1832 ino.ulInode = ulInode;
\r
1833 ret = RedInodeMount(&ino, FTYPE_FILE, true);
\r
1836 #if RESERVED_BLOCKS > 0U
\r
1837 gpRedCoreVol->fUseReservedBlocks = (ullSize < ino.pInodeBuf->ullSize);
\r
1840 ret = RedInodeDataTruncate(&ino, ullSize);
\r
1842 #if RESERVED_BLOCKS > 0U
\r
1843 gpRedCoreVol->fUseReservedBlocks = false;
\r
1846 RedInodePut(&ino, (ret == 0) ? (uint8_t)(IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME) : 0U);
\r
1852 #endif /* TRUNCATE_SUPPORTED */
\r
1855 #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_READDIR == 1)
\r
1856 /** @brief Read from a directory.
\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
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
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
1870 @param pulInode On successful return, populated with the inode number of the
\r
1871 next directory entry.
\r
1873 @return A negated ::REDSTATUS code indicating the operation result.
\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
1882 REDSTATUS RedCoreDirRead(
\r
1886 uint32_t *pulInode)
\r
1890 if(!gpRedVolume->fMounted)
\r
1892 ret = -RED_EINVAL;
\r
1898 ino.ulInode = ulInode;
\r
1899 ret = RedInodeMount(&ino, FTYPE_DIR, false);
\r
1903 ret = RedDirEntryRead(&ino, pulPos, pszName, pulInode);
\r
1905 #if (REDCONF_ATIME == 1) && (REDCONF_READ_ONLY == 0)
\r
1906 if((ret == 0) && !gpRedVolume->fReadOnly)
\r
1908 ret = RedInodeBranch(&ino);
\r
1911 RedInodePut(&ino, ((ret == 0) && !gpRedVolume->fReadOnly) ? IPUT_UPDATE_ATIME : 0U);
\r
1913 RedInodePut(&ino, 0U);
\r
1920 #endif /* (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_READDIR == 1) */
\r