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 directory operations.
\r
30 #if REDCONF_API_POSIX == 1
\r
32 #include <redcore.h>
\r
35 #define DIR_INDEX_INVALID UINT32_MAX
\r
37 #if (REDCONF_NAME_MAX % 4U) != 0U
\r
38 #define DIRENT_PADDING (4U - (REDCONF_NAME_MAX % 4U))
\r
40 #define DIRENT_PADDING (0U)
\r
42 #define DIRENT_SIZE (4U + REDCONF_NAME_MAX + DIRENT_PADDING)
\r
43 #define DIRENTS_PER_BLOCK (REDCONF_BLOCK_SIZE / DIRENT_SIZE)
\r
44 #define DIRENTS_MAX (uint32_t)REDMIN(UINT32_MAX, UINT64_SUFFIX(1) * INODE_DATA_BLOCKS * DIRENTS_PER_BLOCK)
\r
47 /** @brief On-disk directory entry.
\r
51 /** The inode number that the directory entry points at. If the directory
\r
52 entry is available, this holds INODE_INVALID.
\r
56 /** The name of the directory entry. For names shorter than
\r
57 REDCONF_NAME_MAX, unused bytes in the array are zeroed. For names of
\r
58 the maximum length, the string is not null terminated.
\r
60 char acName[REDCONF_NAME_MAX];
\r
62 #if DIRENT_PADDING > 0U
\r
63 /** Unused padding so that ulInode is always aligned on a four-byte
\r
66 uint8_t abPadding[DIRENT_PADDING];
\r
71 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RENAME == 1)
\r
72 static REDSTATUS DirCyclicRenameCheck(uint32_t ulSrcInode, const CINODE *pDstPInode);
\r
74 #if REDCONF_READ_ONLY == 0
\r
75 static REDSTATUS DirEntryWrite(CINODE *pPInode, uint32_t ulIdx, uint32_t ulInode, const char *pszName, uint32_t ulNameLen);
\r
76 static uint64_t DirEntryIndexToOffset(uint32_t ulIdx);
\r
78 static uint32_t DirOffsetToEntryIndex(uint64_t ullOffset);
\r
81 #if REDCONF_READ_ONLY == 0
\r
82 /** @brief Create a new entry in a directory.
\r
84 @param pPInode A pointer to the cached inode structure of the directory
\r
85 to which the new entry will be added.
\r
86 @param pszName The name to be given to the new entry, terminated by a
\r
87 null or a path separator.
\r
88 @param ulInode The inode number the new name will point at.
\r
90 @return A negated ::REDSTATUS code indicating the operation result.
\r
92 @retval 0 Operation was successful.
\r
93 @retval -RED_EIO A disk I/O error occurred.
\r
94 @retval -RED_ENOSPC There is not enough space on the volume to
\r
95 create the new directory entry; or the directory
\r
97 @retval -RED_ENOTDIR @p pPInode is not a directory.
\r
98 @retval -RED_ENAMETOOLONG @p pszName is too long.
\r
99 @retval -RED_EEXIST @p pszName already exists in @p ulPInode.
\r
100 @retval -RED_EINVAL @p pPInode is not a mounted dirty cached inode
\r
101 structure; or @p pszName is not a valid name.
\r
103 REDSTATUS RedDirEntryCreate(
\r
105 const char *pszName,
\r
110 if(!CINODE_IS_DIRTY(pPInode) || (pszName == NULL) || !INODE_IS_VALID(ulInode))
\r
114 else if(!pPInode->fDirectory)
\r
116 ret = -RED_ENOTDIR;
\r
120 uint32_t ulNameLen = RedNameLen(pszName);
\r
122 if(ulNameLen == 0U)
\r
126 else if(ulNameLen > REDCONF_NAME_MAX)
\r
128 ret = -RED_ENAMETOOLONG;
\r
132 uint32_t ulEntryIdx;
\r
134 ret = RedDirEntryLookup(pPInode, pszName, &ulEntryIdx, NULL);
\r
139 else if(ret == -RED_ENOENT)
\r
141 if(ulEntryIdx == DIR_INDEX_INVALID)
\r
152 /* Unexpected error, no action.
\r
158 ret = DirEntryWrite(pPInode, ulEntryIdx, ulInode, pszName, ulNameLen);
\r
165 #endif /* REDCONF_READ_ONLY == 0 */
\r
168 #if DELETE_SUPPORTED
\r
169 /** @brief Delete an existing directory entry.
\r
171 @param pPInode A pointer to the cached inode structure of the directory
\r
172 containing the entry to be deleted.
\r
173 @param ulDeleteIdx Position within the directory of the entry to be
\r
176 @return A negated ::REDSTATUS code indicating the operation result.
\r
178 @retval 0 Operation was successful.
\r
179 @retval -RED_EIO A disk I/O error occurred.
\r
180 @retval -RED_ENOSPC The file system does not have enough space to modify
\r
181 the parent directory to perform the deletion.
\r
182 @retval -RED_ENOTDIR @p pPInode is not a directory.
\r
183 @retval -RED_EINVAL @p pPInode is not a mounted dirty cached inode
\r
184 structure; or @p ulIdx is out of range.
\r
186 REDSTATUS RedDirEntryDelete(
\r
188 uint32_t ulDeleteIdx)
\r
192 if(!CINODE_IS_DIRTY(pPInode) || (ulDeleteIdx >= DIRENTS_MAX))
\r
196 else if(!pPInode->fDirectory)
\r
198 ret = -RED_ENOTDIR;
\r
200 else if((DirEntryIndexToOffset(ulDeleteIdx) + DIRENT_SIZE) == pPInode->pInodeBuf->ullSize)
\r
202 uint32_t ulTruncIdx = ulDeleteIdx;
\r
203 bool fDone = false;
\r
205 /* We are deleting the last dirent in the directory, so search
\r
206 backwards to find the last populated dirent, allowing us to truncate
\r
207 the directory to that point.
\r
209 while((ret == 0) && (ulTruncIdx > 0U) && !fDone)
\r
211 ret = RedInodeDataSeekAndRead(pPInode, ulTruncIdx / DIRENTS_PER_BLOCK);
\r
215 const DIRENT *pDirents = CAST_CONST_DIRENT_PTR(pPInode->pbData);
\r
216 uint32_t ulBlockIdx = (ulTruncIdx - 1U) % DIRENTS_PER_BLOCK;
\r
220 if(pDirents[ulBlockIdx].ulInode != INODE_INVALID)
\r
228 } while(ulBlockIdx != UINT32_MAX);
\r
230 else if(ret == -RED_ENODATA)
\r
234 REDASSERT((ulTruncIdx % DIRENTS_PER_BLOCK) == 0U);
\r
235 ulTruncIdx -= DIRENTS_PER_BLOCK;
\r
239 /* Unexpected error, loop will terminate; nothing else
\r
245 /* Truncate the directory, deleting the requested entry and any empty
\r
246 dirents at the end of the directory.
\r
250 ret = RedInodeDataTruncate(pPInode, DirEntryIndexToOffset(ulTruncIdx));
\r
255 /* The dirent to delete is not the last entry in the directory, so just
\r
258 ret = DirEntryWrite(pPInode, ulDeleteIdx, INODE_INVALID, "", 0U);
\r
263 #endif /* DELETE_SUPPORTED */
\r
266 /** @brief Perform a case-sensitive search of a directory for a given name.
\r
268 If found, then position of the entry within the directory and the inode
\r
269 number it points to are returned. As an optimization for directory entry
\r
270 creation, in the case where the requested entry does not exist, the position
\r
271 of the first available (unused) entry is returned.
\r
273 @param pPInode A pointer to the cached inode structure of the directory
\r
275 @param pszName The name of the desired entry, terminated by either a
\r
276 null or a path separator.
\r
277 @param pulEntryIdx On successful return, meaning that the desired entry
\r
278 exists, populated with the position of the entry. If
\r
279 returning an -RED_ENOENT error, populated with the
\r
280 position of the first available entry, or set to
\r
281 DIR_INDEX_INVALID if the directory is full. Optional.
\r
282 @param pulInode On successful return, populated with the inode number
\r
283 that the name points to. Optional; may be `NULL`.
\r
285 @return A negated ::REDSTATUS code indicating the operation result.
\r
287 @retval 0 Operation was successful.
\r
288 @retval -RED_EIO A disk I/O error occurred.
\r
289 @retval -RED_ENOENT @p pszName does not name an existing file or
\r
291 @retval -RED_ENOTDIR @p pPInode is not a directory.
\r
292 @retval -RED_EINVAL @p pPInode is not a mounted cached inode
\r
293 structure; or @p pszName is not a valid name; or
\r
294 @p pulEntryIdx is `NULL`.
\r
295 @retval -RED_ENAMETOOLONG @p pszName is too long.
\r
297 REDSTATUS RedDirEntryLookup(
\r
299 const char *pszName,
\r
300 uint32_t *pulEntryIdx,
\r
301 uint32_t *pulInode)
\r
305 if(!CINODE_IS_MOUNTED(pPInode) || (pszName == NULL))
\r
309 else if(!pPInode->fDirectory)
\r
311 ret = -RED_ENOTDIR;
\r
315 uint32_t ulNameLen = RedNameLen(pszName);
\r
317 if(ulNameLen == 0U)
\r
321 else if(ulNameLen > REDCONF_NAME_MAX)
\r
323 ret = -RED_ENAMETOOLONG;
\r
327 uint32_t ulIdx = 0U;
\r
328 uint32_t ulDirentCount = DirOffsetToEntryIndex(pPInode->pInodeBuf->ullSize);
\r
329 uint32_t ulFreeIdx = DIR_INDEX_INVALID; /* Index of first free dirent. */
\r
331 /* Loop over the directory blocks, searching each block for a
\r
332 dirent that matches the given name.
\r
334 while((ret == 0) && (ulIdx < ulDirentCount))
\r
336 ret = RedInodeDataSeekAndRead(pPInode, ulIdx / DIRENTS_PER_BLOCK);
\r
340 const DIRENT *pDirents = CAST_CONST_DIRENT_PTR(pPInode->pbData);
\r
341 uint32_t ulBlockLastIdx = REDMIN(DIRENTS_PER_BLOCK, ulDirentCount - ulIdx);
\r
342 uint32_t ulBlockIdx;
\r
344 for(ulBlockIdx = 0U; ulBlockIdx < ulBlockLastIdx; ulBlockIdx++)
\r
346 const DIRENT *pDirent = &pDirents[ulBlockIdx];
\r
348 if(pDirent->ulInode != INODE_INVALID)
\r
350 /* The name in the dirent will not be null
\r
351 terminated if it is of the maximum length, so
\r
352 use a bounded string compare and then make sure
\r
353 there is nothing more to the name.
\r
355 if( (RedStrNCmp(pDirent->acName, pszName, ulNameLen) == 0)
\r
356 && ((ulNameLen == REDCONF_NAME_MAX) || (pDirent->acName[ulNameLen] == '\0')))
\r
358 /* Found a matching dirent, stop and return its
\r
361 if(pulInode != NULL)
\r
363 *pulInode = pDirent->ulInode;
\r
365 #ifdef REDCONF_ENDIAN_SWAP
\r
366 *pulInode = RedRev32(*pulInode);
\r
370 ulIdx += ulBlockIdx;
\r
374 else if(ulFreeIdx == DIR_INDEX_INVALID)
\r
376 ulFreeIdx = ulIdx + ulBlockIdx;
\r
380 /* The directory entry is free, but we already found a free one, so there's
\r
381 nothing to do here.
\r
386 if(ulBlockIdx < ulBlockLastIdx)
\r
388 /* If we broke out of the for loop, we found a matching
\r
389 dirent and can stop the search.
\r
394 ulIdx += ulBlockLastIdx;
\r
396 else if(ret == -RED_ENODATA)
\r
398 if(ulFreeIdx == DIR_INDEX_INVALID)
\r
404 ulIdx += DIRENTS_PER_BLOCK;
\r
408 /* Unexpected error, let the loop terminate, no action
\r
416 /* If we made it all the way to the end of the directory
\r
417 without stopping, then the given name does not exist in the
\r
420 if(ulIdx == ulDirentCount)
\r
422 /* If the directory had no sparse dirents, then the first
\r
423 free dirent is beyond the end of the directory. If the
\r
424 directory is already the maximum size, then there is no
\r
427 if((ulFreeIdx == DIR_INDEX_INVALID) && (ulDirentCount < DIRENTS_MAX))
\r
429 ulFreeIdx = ulDirentCount;
\r
437 if(pulEntryIdx != NULL)
\r
439 *pulEntryIdx = ulIdx;
\r
449 #if (REDCONF_API_POSIX_READDIR == 1) || (REDCONF_CHECKER == 1)
\r
450 /** @brief Read the next entry from a directory, given a starting index.
\r
452 @param pInode A pointer to the cached inode structure of the directory to
\r
454 @param pulIdx On entry, the directory index to start reading from. On
\r
455 successful return, populated with the directory index to use
\r
456 for subsequent reads. On -RED_ENOENT return, populated with
\r
457 the directory index immediately following the last valid
\r
459 @param pszName On successful return, populated with the name of the next
\r
460 directory entry. Buffer must be at least
\r
461 REDCONF_NAME_MAX + 1 in size, to store the maximum name
\r
462 length plus a null terminator.
\r
463 @param pulInode On successful return, populated with the inode number
\r
464 pointed at by the next directory entry.
\r
466 @return A negated ::REDSTATUS code indicating the operation result.
\r
468 @retval 0 Operation was successful.
\r
469 @retval -RED_EIO A disk I/O error occurred.
\r
470 @retval -RED_ENOENT There are no more entries in the directory.
\r
471 @retval -RED_ENOTDIR @p pPInode is not a directory.
\r
472 @retval -RED_EINVAL @p pPInode is not a mounted cached inode structure;
\r
473 or @p pszName is `NULL`; or @p pulIdx is `NULL`; or
\r
474 @p pulInode is `NULL`.
\r
476 REDSTATUS RedDirEntryRead(
\r
480 uint32_t *pulInode)
\r
484 if(!CINODE_IS_MOUNTED(pPInode) || (pulIdx == NULL) || (pszName == NULL) || (pulInode == NULL))
\r
488 else if(!pPInode->fDirectory)
\r
490 ret = -RED_ENOTDIR;
\r
494 uint32_t ulIdx = *pulIdx;
\r
495 uint32_t ulDirentCount = DirOffsetToEntryIndex(pPInode->pInodeBuf->ullSize);
\r
497 /* Starting either at the beginning of the directory or where we left
\r
498 off, loop over the directory blocks, searching each block for a
\r
499 non-sparse dirent to return as the next entry in the directory.
\r
501 while((ret == 0) && (ulIdx < ulDirentCount))
\r
503 uint32_t ulBlockOffset = ulIdx / DIRENTS_PER_BLOCK;
\r
505 ret = RedInodeDataSeekAndRead(pPInode, ulBlockOffset);
\r
509 const DIRENT *pDirents = CAST_CONST_DIRENT_PTR(pPInode->pbData);
\r
510 uint32_t ulBlockLastIdx = REDMIN(DIRENTS_PER_BLOCK, ulDirentCount - (ulBlockOffset * DIRENTS_PER_BLOCK));
\r
511 uint32_t ulBlockIdx;
\r
513 for(ulBlockIdx = ulIdx % DIRENTS_PER_BLOCK; ulBlockIdx < ulBlockLastIdx; ulBlockIdx++)
\r
515 if(pDirents[ulBlockIdx].ulInode != INODE_INVALID)
\r
517 *pulIdx = ulIdx + 1U;
\r
518 RedStrNCpy(pszName, pDirents[ulBlockIdx].acName, REDCONF_NAME_MAX);
\r
519 pszName[REDCONF_NAME_MAX] = '\0';
\r
521 *pulInode = pDirents[ulBlockIdx].ulInode;
\r
523 #ifdef REDCONF_ENDIAN_SWAP
\r
524 *pulInode = RedRev32(*pulInode);
\r
532 if(ulBlockIdx < ulBlockLastIdx)
\r
534 REDASSERT(ulIdx <= ulDirentCount);
\r
538 else if(ret == -RED_ENODATA)
\r
540 ulIdx += DIRENTS_PER_BLOCK - (ulIdx % DIRENTS_PER_BLOCK);
\r
545 /* Unexpected error, loop will terminate; nothing else to do.
\r
549 REDASSERT(ulIdx <= ulDirentCount);
\r
552 if((ret == 0) && (ulIdx >= ulDirentCount))
\r
554 *pulIdx = ulDirentCount;
\r
564 #if (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RENAME == 1)
\r
565 /** Rename a directory entry.
\r
567 @param pSrcPInode The inode of the directory containing @p pszSrcName.
\r
568 @param pszSrcName The name of the directory entry to be renamed.
\r
569 @param pSrcInode On successful return, populated with the inode of the
\r
571 @param pDstPInode The inode of the directory in which @p pszDstName will
\r
572 be created or replaced.
\r
573 @param pszDstName The name of the directory entry to be created or
\r
575 @param pDstInode On successful return, if the destination previously
\r
576 existed, populated with the inode previously pointed to
\r
577 by the destination. This may be the same as the source
\r
578 inode. If the destination did not exist, populated with
\r
581 @return A negated ::REDSTATUS code indicating the operation result.
\r
583 @retval 0 Operation was successful.
\r
584 @retval -RED_EEXIST #REDCONF_RENAME_ATOMIC is false and the
\r
585 destination name exists.
\r
586 @retval -RED_EINVAL @p pSrcPInode is not a mounted dirty cached
\r
587 inode structure; or @p pSrcInode is `NULL`; or
\r
588 @p pszSrcName is not a valid name; or
\r
589 @p pDstPInode is not a mounted dirty cached
\r
590 inode structure; or @p pDstInode is `NULL`; or
\r
591 @p pszDstName is not a valid name.
\r
592 @retval -RED_EIO A disk I/O error occurred.
\r
593 @retval -RED_EISDIR The destination name exists and is a directory,
\r
594 and the source name is a non-directory.
\r
595 @retval -RED_ENAMETOOLONG Either @p pszSrcName or @p pszDstName is longer
\r
596 than #REDCONF_NAME_MAX.
\r
597 @retval -RED_ENOENT The source name is not an existing entry; or
\r
598 either @p pszSrcName or @p pszDstName point to
\r
600 @retval -RED_ENOTDIR @p pSrcPInode is not a directory; or
\r
601 @p pDstPInode is not a directory; or the source
\r
602 name is a directory and the destination name is
\r
604 @retval -RED_ENOTEMPTY The destination name is a directory which is not
\r
606 @retval -RED_ENOSPC The file system does not have enough space to
\r
607 extend the @p ulDstPInode directory.
\r
608 @retval -RED_EROFS The directory to be removed resides on a
\r
609 read-only file system.
\r
611 REDSTATUS RedDirEntryRename(
\r
612 CINODE *pSrcPInode,
\r
613 const char *pszSrcName,
\r
615 CINODE *pDstPInode,
\r
616 const char *pszDstName,
\r
621 if( !CINODE_IS_DIRTY(pSrcPInode)
\r
622 || (pszSrcName == NULL)
\r
623 || (pSrcInode == NULL)
\r
624 || !CINODE_IS_DIRTY(pDstPInode)
\r
625 || (pszDstName == NULL)
\r
626 || (pDstInode == NULL))
\r
630 else if(!pSrcPInode->fDirectory || !pDstPInode->fDirectory)
\r
632 ret = -RED_ENOTDIR;
\r
636 uint32_t ulDstIdx = 0U; /* Init'd to quiet warnings. */
\r
639 /* Look up the source and destination names.
\r
641 ret = RedDirEntryLookup(pSrcPInode, pszSrcName, &ulSrcIdx, &pSrcInode->ulInode);
\r
645 ret = RedDirEntryLookup(pDstPInode, pszDstName, &ulDstIdx, &pDstInode->ulInode);
\r
647 if(ret == -RED_ENOENT)
\r
649 if(ulDstIdx == DIR_INDEX_INVALID)
\r
655 #if REDCONF_RENAME_ATOMIC == 1
\r
656 pDstInode->ulInode = INODE_INVALID;
\r
661 #if REDCONF_RENAME_ATOMIC == 0
\r
668 /* Nothing to do here, just propagate the error.
\r
674 #if REDCONF_RENAME_ATOMIC == 1
\r
675 /* If both names point to the same inode, POSIX says to do nothing to
\r
678 if((ret == 0) && (pSrcInode->ulInode != pDstInode->ulInode))
\r
683 ret = RedInodeMount(pSrcInode, FTYPE_EITHER, true);
\r
685 #if REDCONF_RENAME_ATOMIC == 1
\r
686 if((ret == 0) && (pDstInode->ulInode != INODE_INVALID))
\r
688 /* Source and destination must be the same type (file/dir).
\r
690 ret = RedInodeMount(pDstInode, pSrcInode->fDirectory ? FTYPE_DIR : FTYPE_FILE, true);
\r
692 /* If renaming directories, the destination must be empty.
\r
694 if((ret == 0) && pDstInode->fDirectory && (pDstInode->pInodeBuf->ullSize > 0U))
\r
696 ret = -RED_ENOTEMPTY;
\r
701 /* If we are renaming a directory, make sure the rename isn't
\r
702 cyclic (e.g., renaming "foo" into "foo/bar").
\r
704 if((ret == 0) && pSrcInode->fDirectory)
\r
706 ret = DirCyclicRenameCheck(pSrcInode->ulInode, pDstPInode);
\r
711 ret = DirEntryWrite(pDstPInode, ulDstIdx, pSrcInode->ulInode, pszDstName, RedNameLen(pszDstName));
\r
716 ret = RedDirEntryDelete(pSrcPInode, ulSrcIdx);
\r
718 if(ret == -RED_ENOSPC)
\r
722 /* If there was not enough space to branch the parent
\r
723 directory inode and data block containin the source
\r
724 entry, revert destination directory entry to its
\r
727 #if REDCONF_RENAME_ATOMIC == 1
\r
728 if(pDstInode->ulInode != INODE_INVALID)
\r
730 ret2 = DirEntryWrite(pDstPInode, ulDstIdx, pDstInode->ulInode, pszDstName, RedNameLen(pszDstName));
\r
735 ret2 = RedDirEntryDelete(pDstPInode, ulDstIdx);
\r
748 pSrcInode->pInodeBuf->ulPInode = pDstPInode->ulInode;
\r
757 /** @brief Check for a cyclic rename.
\r
759 A cyclic rename is renaming a directory into a subdirectory of itself. For
\r
760 example, renaming "a" into "a/b/c/d" is cyclic. These renames must not be
\r
761 allowed since they would corrupt the directory tree.
\r
763 @param ulSrcInode The inode number of the directory being renamed.
\r
764 @param pDstPInode A pointer to the cached inode structure of the directory
\r
765 into which the source is being renamed.
\r
767 @return A negated ::REDSTATUS code indicating the operation result.
\r
769 @retval 0 Operation was successful.
\r
770 @retval -RED_EIO A disk I/O error occurred.
\r
771 @retval -RED_EINVAL The rename is cyclic; or invalid parameters.
\r
772 @retval -RED_ENOTDIR @p pDstPInode is not a directory.
\r
774 static REDSTATUS DirCyclicRenameCheck(
\r
775 uint32_t ulSrcInode,
\r
776 const CINODE *pDstPInode)
\r
780 if(!INODE_IS_VALID(ulSrcInode) || !CINODE_IS_MOUNTED(pDstPInode))
\r
785 else if(ulSrcInode == pDstPInode->ulInode)
\r
789 else if(!pDstPInode->fDirectory)
\r
791 ret = -RED_ENOTDIR;
\r
796 /* Used to prevent infinite loop in case of corrupted directory
\r
799 uint32_t ulIteration = 0U;
\r
801 NextParent.ulInode = pDstPInode->pInodeBuf->ulPInode;
\r
803 while( (NextParent.ulInode != ulSrcInode)
\r
804 && (NextParent.ulInode != INODE_ROOTDIR)
\r
805 && (NextParent.ulInode != INODE_INVALID)
\r
806 && (ulIteration < gpRedVolConf->ulInodeCount))
\r
808 ret = RedInodeMount(&NextParent, FTYPE_DIR, false);
\r
814 NextParent.ulInode = NextParent.pInodeBuf->ulPInode;
\r
816 RedInodePut(&NextParent, 0U);
\r
821 if((ret == 0) && (ulIteration == gpRedVolConf->ulInodeCount))
\r
827 if((ret == 0) && (ulSrcInode == NextParent.ulInode))
\r
835 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_RENAME == 1) */
\r
838 #if REDCONF_READ_ONLY == 0
\r
839 /** @brief Update the contents of a directory entry.
\r
841 @param pPInode A pointer to the cached inode structure of the directory
\r
842 whose entry is being written.
\r
843 @param ulIdx The index of the directory entry to write.
\r
844 @param ulInode The inode number the directory entry is to point at.
\r
845 @param pszName The name of the directory entry.
\r
846 @param ulNameLen The length of @p pszName.
\r
848 @return A negated ::REDSTATUS code indicating the operation result.
\r
850 @retval 0 Operation was successful.
\r
851 @retval -RED_EIO A disk I/O error occurred.
\r
852 @retval -RED_ENOSPC There is not enough space on the volume to write the
\r
854 @retval -RED_ENOTDIR @p pPInode is not a directory.
\r
855 @retval -RED_EINVAL Invalid parameters.
\r
857 static REDSTATUS DirEntryWrite(
\r
861 const char *pszName,
\r
862 uint32_t ulNameLen)
\r
866 if( !CINODE_IS_DIRTY(pPInode)
\r
867 || (ulIdx >= DIRENTS_MAX)
\r
868 || (!INODE_IS_VALID(ulInode) && (ulInode != INODE_INVALID))
\r
869 || (pszName == NULL)
\r
870 || (ulNameLen > REDCONF_NAME_MAX)
\r
871 || ((ulNameLen == 0U) != (ulInode == INODE_INVALID)))
\r
876 else if(!pPInode->fDirectory)
\r
878 ret = -RED_ENOTDIR;
\r
882 uint64_t ullOffset = DirEntryIndexToOffset(ulIdx);
\r
883 uint32_t ulLen = DIRENT_SIZE;
\r
886 RedMemSet(&de, 0U, sizeof(de));
\r
888 de.ulInode = ulInode;
\r
890 #ifdef REDCONF_ENDIAN_SWAP
\r
891 de.ulInode = RedRev32(de.ulInode);
\r
894 RedStrNCpy(de.acName, pszName, ulNameLen);
\r
896 ret = RedInodeDataWrite(pPInode, ullOffset, &ulLen, &de);
\r
903 /** @brief Convert a directory entry index to a byte offset.
\r
905 @param ulIdx Directory entry index.
\r
907 @return Byte offset in the directory corresponding with ulIdx.
\r
909 static uint64_t DirEntryIndexToOffset(
\r
912 uint32_t ulBlock = ulIdx / DIRENTS_PER_BLOCK;
\r
913 uint32_t ulOffsetInBlock = ulIdx % DIRENTS_PER_BLOCK;
\r
914 uint64_t ullOffset;
\r
916 REDASSERT(ulIdx < DIRENTS_MAX);
\r
918 ullOffset = (uint64_t)ulBlock << BLOCK_SIZE_P2;
\r
919 ullOffset += (uint64_t)ulOffsetInBlock * DIRENT_SIZE;
\r
923 #endif /* REDCONF_READ_ONLY == 0 */
\r
926 /** @brief Convert a byte offset to a directory entry index.
\r
928 @param ullOffset Byte offset in the directory.
\r
930 @return Directory entry index corresponding with @p ullOffset.
\r
932 static uint32_t DirOffsetToEntryIndex(
\r
933 uint64_t ullOffset)
\r
937 REDASSERT(ullOffset < INODE_SIZE_MAX);
\r
938 REDASSERT(((uint32_t)(ullOffset & (REDCONF_BLOCK_SIZE - 1U)) % DIRENT_SIZE) == 0U);
\r
940 /* Avoid doing any 64-bit divides.
\r
942 ulIdx = (uint32_t)(ullOffset >> BLOCK_SIZE_P2) * DIRENTS_PER_BLOCK;
\r
943 ulIdx += (uint32_t)(ullOffset & (REDCONF_BLOCK_SIZE - 1U)) / DIRENT_SIZE;
\r
949 #endif /* REDCONF_API_POSIX == 1 */
\r