Initial version
This commit is contained in:
448
FreeRTOSv10.2.1/FreeRTOS-Plus/Source/Reliance-Edge/posix/path.c
Normal file
448
FreeRTOSv10.2.1/FreeRTOS-Plus/Source/Reliance-Edge/posix/path.c
Normal file
@ -0,0 +1,448 @@
|
||||
/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
|
||||
|
||||
Copyright (c) 2014-2015 Datalight, Inc.
|
||||
All Rights Reserved Worldwide.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; use version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/* Businesses and individuals that for commercial or other reasons cannot
|
||||
comply with the terms of the GPLv2 license may obtain a commercial license
|
||||
before incorporating Reliance Edge into proprietary software for
|
||||
distribution in any form. Visit http://www.datalight.com/reliance-edge for
|
||||
more information.
|
||||
*/
|
||||
/** @file
|
||||
@brief Implements path utilities for the POSIX-like API layer.
|
||||
*/
|
||||
#include <redfs.h>
|
||||
|
||||
#if REDCONF_API_POSIX == 1
|
||||
|
||||
#include <redcoreapi.h>
|
||||
#include <redvolume.h>
|
||||
#include <redposix.h>
|
||||
#include <redpath.h>
|
||||
|
||||
|
||||
static bool IsRootDir(const char *pszLocalPath);
|
||||
static bool PathHasMoreNames(const char *pszPathIdx);
|
||||
|
||||
|
||||
/** @brief Split a path into its component parts: a volume and a volume-local
|
||||
path.
|
||||
|
||||
@param pszPath The path to split.
|
||||
@param pbVolNum On successful return, if non-NULL, populated with
|
||||
the volume number extracted from the path.
|
||||
@param ppszLocalPath On successful return, populated with the
|
||||
volume-local path: the path stripped of any volume
|
||||
path prefixing. If this parameter is NULL, that
|
||||
indicates there should be no local path, and any
|
||||
characters beyond the prefix (other than path
|
||||
separators) are treated as an error.
|
||||
|
||||
@return A negated ::REDSTATUS code indicating the operation result.
|
||||
|
||||
@retval 0 Operation was successful.
|
||||
@retval -RED_EINVAL @p pszPath is `NULL`.
|
||||
@retval -RED_ENOENT @p pszPath could not be matched to any volume; or
|
||||
@p ppszLocalPath is NULL but @p pszPath includes a local
|
||||
path.
|
||||
*/
|
||||
REDSTATUS RedPathSplit(
|
||||
const char *pszPath,
|
||||
uint8_t *pbVolNum,
|
||||
const char **ppszLocalPath)
|
||||
{
|
||||
REDSTATUS ret = 0;
|
||||
|
||||
if(pszPath == NULL)
|
||||
{
|
||||
ret = -RED_EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *pszLocalPath = pszPath;
|
||||
uint8_t bMatchVol = UINT8_MAX;
|
||||
uint32_t ulMatchLen = 0U;
|
||||
uint8_t bDefaultVolNum = UINT8_MAX;
|
||||
uint8_t bVolNum;
|
||||
|
||||
for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)
|
||||
{
|
||||
const char *pszPrefix = gaRedVolConf[bVolNum].pszPathPrefix;
|
||||
uint32_t ulPrefixLen = RedStrLen(pszPrefix);
|
||||
|
||||
if(ulPrefixLen == 0U)
|
||||
{
|
||||
/* A volume with a path prefix of an empty string is the
|
||||
default volume, used when the path does not match the
|
||||
prefix of any other volume.
|
||||
|
||||
The default volume should only be found once. During
|
||||
initialization, RedCoreInit() ensures that all volume
|
||||
prefixes are unique (including empty prefixes).
|
||||
*/
|
||||
REDASSERT(bDefaultVolNum == UINT8_MAX);
|
||||
bDefaultVolNum = bVolNum;
|
||||
}
|
||||
/* For a path to match, it must either be the prefix exactly, or
|
||||
be followed by a path separator character. Thus, with a volume
|
||||
prefix of "/foo", both "/foo" and "/foo/bar" are matches, but
|
||||
"/foobar" is not.
|
||||
*/
|
||||
else if( (RedStrNCmp(pszPath, pszPrefix, ulPrefixLen) == 0)
|
||||
&& ((pszPath[ulPrefixLen] == '\0') || (pszPath[ulPrefixLen] == REDCONF_PATH_SEPARATOR)))
|
||||
{
|
||||
/* The length of this match should never exactly equal the
|
||||
length of a previous match: that would require a duplicate
|
||||
volume name, which should have been detected during init.
|
||||
*/
|
||||
REDASSERT(ulPrefixLen != ulMatchLen);
|
||||
|
||||
/* If multiple prefixes match, the longest takes precedence.
|
||||
Thus, if there are two prefixes "Flash" and "Flash/Backup",
|
||||
the path "Flash/Backup/" will not be erroneously matched
|
||||
with the "Flash" volume.
|
||||
*/
|
||||
if(ulPrefixLen > ulMatchLen)
|
||||
{
|
||||
bMatchVol = bVolNum;
|
||||
ulMatchLen = ulPrefixLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No match, keep looking.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if(bMatchVol != UINT8_MAX)
|
||||
{
|
||||
/* The path matched a volume path prefix.
|
||||
*/
|
||||
bVolNum = bMatchVol;
|
||||
pszLocalPath = &pszPath[ulMatchLen];
|
||||
}
|
||||
else if(bDefaultVolNum != UINT8_MAX)
|
||||
{
|
||||
/* The path didn't match any of the prefixes, but one of the
|
||||
volumes has a path prefix of "", so an unprefixed path is
|
||||
assigned to that volume.
|
||||
*/
|
||||
bVolNum = bDefaultVolNum;
|
||||
REDASSERT(pszLocalPath == pszPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The path cannot be assigned a volume.
|
||||
*/
|
||||
ret = -RED_ENOENT;
|
||||
}
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
if(pbVolNum != NULL)
|
||||
{
|
||||
*pbVolNum = bVolNum;
|
||||
}
|
||||
|
||||
if(ppszLocalPath != NULL)
|
||||
{
|
||||
*ppszLocalPath = pszLocalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If no local path is expected, then the string should either
|
||||
terminate after the path prefix or the local path should name
|
||||
the root directory. Allowing path separators here means that
|
||||
red_mount("/data/") is OK with a path prefix of "/data".
|
||||
*/
|
||||
if(pszLocalPath[0U] != '\0')
|
||||
{
|
||||
if(!IsRootDir(pszLocalPath))
|
||||
{
|
||||
ret = -RED_ENOENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Lookup the inode named by the given path.
|
||||
|
||||
@param pszLocalPath The path to lookup; this is a local path, without any
|
||||
volume prefix.
|
||||
@param pulInode On successful return, populated with the number of the
|
||||
inode named by @p pszLocalPath.
|
||||
|
||||
@return A negated ::REDSTATUS code indicating the operation result.
|
||||
|
||||
@retval 0 Operation was successful.
|
||||
@retval -RED_EINVAL @p pszLocalPath is `NULL`; or @p pulInode is
|
||||
`NULL`.
|
||||
@retval -RED_EIO A disk I/O error occurred.
|
||||
@retval -RED_ENOENT @p pszLocalPath is an empty string; or
|
||||
@p pszLocalPath does not name an existing file
|
||||
or directory.
|
||||
@retval -RED_ENOTDIR A component of the path other than the last is
|
||||
not a directory.
|
||||
@retval -RED_ENAMETOOLONG The length of a component of @p pszLocalPath is
|
||||
longer than #REDCONF_NAME_MAX.
|
||||
*/
|
||||
REDSTATUS RedPathLookup(
|
||||
const char *pszLocalPath,
|
||||
uint32_t *pulInode)
|
||||
{
|
||||
REDSTATUS ret;
|
||||
|
||||
if((pszLocalPath == NULL) || (pulInode == NULL))
|
||||
{
|
||||
REDERROR();
|
||||
ret = -RED_EINVAL;
|
||||
}
|
||||
else if(pszLocalPath[0U] == '\0')
|
||||
{
|
||||
ret = -RED_ENOENT;
|
||||
}
|
||||
else if(IsRootDir(pszLocalPath))
|
||||
{
|
||||
ret = 0;
|
||||
*pulInode = INODE_ROOTDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ulPInode;
|
||||
const char *pszName;
|
||||
|
||||
ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
ret = RedCoreLookup(ulPInode, pszName, pulInode);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Given a path, return the parent inode number and a pointer to the
|
||||
last component in the path (the name).
|
||||
|
||||
@param pszLocalPath The path to examine; this is a local path, without any
|
||||
volume prefix.
|
||||
@param pulPInode On successful return, populated with the inode number of
|
||||
the parent directory of the last component in the path.
|
||||
For example, with the path "a/b/c", populated with the
|
||||
inode number of "b".
|
||||
@param ppszName On successful return, populated with a pointer to the
|
||||
last component in the path. For example, with the path
|
||||
"a/b/c", populated with a pointer to "c".
|
||||
|
||||
@return A negated ::REDSTATUS code indicating the operation result.
|
||||
|
||||
@retval 0 Operation was successful.
|
||||
@retval -RED_EINVAL @p pszLocalPath is `NULL`; or @p pulPInode is
|
||||
`NULL`; or @p ppszName is `NULL`; or the path
|
||||
names the root directory.
|
||||
@retval -RED_EIO A disk I/O error occurred.
|
||||
@retval -RED_ENOENT @p pszLocalPath is an empty string; or a
|
||||
component of the path other than the last does
|
||||
not exist.
|
||||
@retval -RED_ENOTDIR A component of the path other than the last is
|
||||
not a directory.
|
||||
@retval -RED_ENAMETOOLONG The length of a component of @p pszLocalPath is
|
||||
longer than #REDCONF_NAME_MAX.
|
||||
*/
|
||||
REDSTATUS RedPathToName(
|
||||
const char *pszLocalPath,
|
||||
uint32_t *pulPInode,
|
||||
const char **ppszName)
|
||||
{
|
||||
REDSTATUS ret;
|
||||
|
||||
if((pszLocalPath == NULL) || (pulPInode == NULL) || (ppszName == NULL))
|
||||
{
|
||||
REDERROR();
|
||||
ret = -RED_EINVAL;
|
||||
}
|
||||
else if(IsRootDir(pszLocalPath))
|
||||
{
|
||||
ret = -RED_EINVAL;
|
||||
}
|
||||
else if(pszLocalPath[0U] == '\0')
|
||||
{
|
||||
ret = -RED_ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ulInode = INODE_ROOTDIR;
|
||||
uint32_t ulPInode = INODE_INVALID;
|
||||
uint32_t ulPathIdx = 0U;
|
||||
uint32_t ulLastNameIdx = 0U;
|
||||
|
||||
ret = 0;
|
||||
|
||||
do
|
||||
{
|
||||
uint32_t ulNameLen;
|
||||
|
||||
/* Skip over path separators, to get pszLocalPath[ulPathIdx]
|
||||
pointing at the next name.
|
||||
*/
|
||||
while(pszLocalPath[ulPathIdx] == REDCONF_PATH_SEPARATOR)
|
||||
{
|
||||
ulPathIdx++;
|
||||
}
|
||||
|
||||
if(pszLocalPath[ulPathIdx] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Point ulLastNameIdx at the first character of the name; after
|
||||
we exit the loop, it will point at the first character of the
|
||||
last name in the path.
|
||||
*/
|
||||
ulLastNameIdx = ulPathIdx;
|
||||
|
||||
/* Point ulPInode at the parent inode: either the root inode
|
||||
(first pass) or the inode of the previous name. After we exit
|
||||
the loop, this will point at the parent inode of the last name.
|
||||
*/
|
||||
ulPInode = ulInode;
|
||||
|
||||
ulNameLen = RedNameLen(&pszLocalPath[ulPathIdx]);
|
||||
|
||||
/* Lookup the inode of the name, unless we are at the last name in
|
||||
the path: we don't care whether the last name exists or not.
|
||||
*/
|
||||
if(PathHasMoreNames(&pszLocalPath[ulPathIdx + ulNameLen]))
|
||||
{
|
||||
ret = RedCoreLookup(ulPInode, &pszLocalPath[ulPathIdx], &ulInode);
|
||||
}
|
||||
|
||||
/* Move on to the next path element.
|
||||
*/
|
||||
if(ret == 0)
|
||||
{
|
||||
ulPathIdx += ulNameLen;
|
||||
}
|
||||
}
|
||||
while(ret == 0);
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
*pulPInode = ulPInode;
|
||||
*ppszName = &pszLocalPath[ulLastNameIdx];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Determine whether a path names the root directory.
|
||||
|
||||
@param pszLocalPath The path to examine; this is a local path, without any
|
||||
volume prefix.
|
||||
|
||||
@return Returns whether @p pszLocalPath names the root directory.
|
||||
|
||||
@retval true @p pszLocalPath names the root directory.
|
||||
@retval false @p pszLocalPath does not name the root directory.
|
||||
*/
|
||||
static bool IsRootDir(
|
||||
const char *pszLocalPath)
|
||||
{
|
||||
bool fRet;
|
||||
|
||||
if(pszLocalPath == NULL)
|
||||
{
|
||||
REDERROR();
|
||||
fRet = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ulIdx = 0U;
|
||||
|
||||
/* A string containing nothing but path separators (usually only one)
|
||||
names the root directory. An empty string does *not* name the root
|
||||
directory, since in POSIX empty strings typically elicit -RED_ENOENT
|
||||
errors.
|
||||
*/
|
||||
while(pszLocalPath[ulIdx] == REDCONF_PATH_SEPARATOR)
|
||||
{
|
||||
ulIdx++;
|
||||
}
|
||||
|
||||
fRet = (ulIdx > 0U) && (pszLocalPath[ulIdx] == '\0');
|
||||
}
|
||||
|
||||
return fRet;
|
||||
}
|
||||
|
||||
|
||||
/** @brief Determine whether there are more names in a path.
|
||||
|
||||
Example | Result
|
||||
------- | ------
|
||||
"" false
|
||||
"/" false
|
||||
"//" false
|
||||
"a" true
|
||||
"/a" true
|
||||
"//a" true
|
||||
|
||||
@param pszPathIdx The path to examine, incremented to the point of
|
||||
interest.
|
||||
|
||||
@return Returns whether there are more names in @p pszPathIdx.
|
||||
|
||||
@retval true @p pszPathIdx has more names.
|
||||
@retval false @p pszPathIdx has no more names.
|
||||
*/
|
||||
static bool PathHasMoreNames(
|
||||
const char *pszPathIdx)
|
||||
{
|
||||
bool fRet;
|
||||
|
||||
if(pszPathIdx == NULL)
|
||||
{
|
||||
REDERROR();
|
||||
fRet = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ulIdx = 0U;
|
||||
|
||||
while(pszPathIdx[ulIdx] == REDCONF_PATH_SEPARATOR)
|
||||
{
|
||||
ulIdx++;
|
||||
}
|
||||
|
||||
fRet = pszPathIdx[ulIdx] != '\0';
|
||||
}
|
||||
|
||||
return fRet;
|
||||
}
|
||||
|
||||
#endif /* REDCONF_API_POSIX */
|
||||
|
3088
FreeRTOSv10.2.1/FreeRTOS-Plus/Source/Reliance-Edge/posix/posix.c
Normal file
3088
FreeRTOSv10.2.1/FreeRTOS-Plus/Source/Reliance-Edge/posix/posix.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user