fs/hostfs: add link, symlink, readlink and lstat support

Implemented link(), symlink(), readlink() and lstat() in hostfs.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
This commit is contained in:
zhengyu16 2025-11-25 22:19:48 +08:00 committed by GUIDINGLI
parent c7a78c01c1
commit 86193c95ca
9 changed files with 568 additions and 24 deletions

View file

@ -370,3 +370,31 @@ int host_chstat(const char *path, const struct stat *buf, int flags)
{
return -ENOSYS;
}
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
/* ARM semihosting doesn't support hard links */
return -ENOSYS;
}
int host_symlink(const char *target, const char *linkpath)
{
/* ARM semihosting doesn't support symbolic links */
return -ENOSYS;
}
ssize_t host_readlink(const char *path, char *buf, size_t bufsize)
{
/* ARM semihosting doesn't support symbolic links */
return -ENOSYS;
}
int host_lstat(const char *path, struct stat *buf)
{
return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -370,3 +370,25 @@ int host_chstat(const char *path, const struct stat *buf, int flags)
{
return -ENOSYS;
}
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
return -ENOSYS;
}
int host_symlink(const char *target, const char *linkpath)
{
return -ENOSYS;
}
ssize_t host_readlink(const char *path, char *buf, size_t bufsize)
{
return -ENOSYS;
}
int host_lstat(const char *path, struct stat *buf)
{
return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -368,3 +368,25 @@ int host_chstat(const char *path, const struct stat *buf, int flags)
{
return -ENOSYS;
}
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
return -ENOSYS;
}
int host_symlink(const char *target, const char *linkpath)
{
return -ENOSYS;
}
ssize_t host_readlink(const char *path, char *buf, size_t bufsize)
{
return -ENOSYS;
}
int host_lstat(const char *path, struct stat *buf)
{
return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -75,9 +75,11 @@ NXSYMBOLS(getpid)
NXSYMBOLS(getsockopt)
NXSYMBOLS(if_nametoindex)
NXSYMBOLS(ioctl)
NXSYMBOLS(link)
NXSYMBOLS(listen)
NXSYMBOLS(longjmp)
NXSYMBOLS(lseek)
NXSYMBOLS(lstat)
NXSYMBOLS(malloc)
NXSYMBOLS(malloc_size)
NXSYMBOLS(malloc_usable_size)
@ -154,6 +156,7 @@ NXSYMBOLS(strchr)
NXSYMBOLS(strerror)
NXSYMBOLS(strlen)
NXSYMBOLS(strtol)
NXSYMBOLS(symlink)
NXSYMBOLS(sysconf)
NXSYMBOLS(syslog)
NXSYMBOLS(system)

View file

@ -820,3 +820,74 @@ int host_chstat(const char *path, const struct nuttx_stat_s *buf, int flags)
return 0;
}
/****************************************************************************
* Name: host_link
****************************************************************************/
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
int ret = link(path1, path2);
if (ret < 0)
{
ret = -errno;
}
return ret;
}
/****************************************************************************
* Name: host_symlink
****************************************************************************/
int host_symlink(const char *target, const char *linkpath)
{
int ret = symlink(target, linkpath);
if (ret < 0)
{
ret = -errno;
}
return ret;
}
/****************************************************************************
* Name: host_readlink
****************************************************************************/
nuttx_ssize_t host_readlink(const char *path, char *buf,
nuttx_size_t bufsize)
{
ssize_t ret = readlink(path, buf, bufsize);
if (ret < 0)
{
ret = -errno;
}
return ret;
}
/****************************************************************************
* Name: host_lstat
****************************************************************************/
int host_lstat(const char *path, struct nuttx_stat_s *buf)
{
struct stat hostbuf;
int ret;
/* Call the host's lstat routine */
ret = lstat(path, &hostbuf);
if (ret < 0)
{
ret = -errno;
}
/* Map the return values */
host_stat_convert(&hostbuf, buf);
return ret;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -461,3 +461,42 @@ int host_chstat(const char *path, const struct nuttx_stat_s *buf, int flags)
{
return -ENOSYS;
}
/****************************************************************************
* Name: host_link
****************************************************************************/
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
return -ENOSYS;
}
/****************************************************************************
* Name: host_symlink
****************************************************************************/
int host_symlink(const char *target, const char *linkpath)
{
return -ENOSYS;
}
/****************************************************************************
* Name: host_readlink
****************************************************************************/
nuttx_ssize_t host_readlink(const char *path, char *buf,
nuttx_size_t bufsize)
{
return -ENOSYS;
}
/****************************************************************************
* Name: host_lstat
****************************************************************************/
int host_lstat(const char *path, struct nuttx_stat_s *buf)
{
return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -256,3 +256,25 @@ int host_chstat(const char *path, const struct stat *buf, int flags)
{
return -ENOSYS;
}
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
return -ENOSYS;
}
int host_symlink(const char *target, const char *linkpath)
{
return -ENOSYS;
}
ssize_t host_readlink(const char *path, char *buf, size_t bufsize)
{
return -ENOSYS;
}
int host_lstat(const char *path, struct stat *buf)
{
return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */

View file

@ -117,6 +117,21 @@ static int hostfs_chstat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR const struct stat *buf, int flags);
#ifdef CONFIG_FS_LINKS
static int hostfs_link(FAR struct inode *mountpt,
FAR const char *relpath1,
FAR const char *relpath2);
static int hostfs_symlink(FAR struct inode *mountpt,
FAR const char *path1,
FAR const char *relpath2);
static ssize_t hostfs_readlink(FAR struct inode *mountpt,
FAR const char *relpath,
FAR char *buf, size_t bufsize);
static int hostfs_lstat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct stat *buf);
#endif
/****************************************************************************
* Public Data
****************************************************************************/
@ -160,6 +175,15 @@ const struct mountpt_operations g_hostfs_operations =
hostfs_rename, /* rename */
hostfs_stat, /* stat */
hostfs_chstat, /* chstat */
NULL, /* syncfs */
NULL, /* ioctldir */
NULL, /* permission */
#ifdef CONFIG_FS_LINKS
hostfs_link, /* link */
hostfs_symlink, /* symlink */
hostfs_readlink, /* readlink */
hostfs_lstat, /* lstat */
#endif
};
/****************************************************************************
@ -237,7 +261,7 @@ static int hostfs_open(FAR struct file *filep, FAR const char *relpath,
FAR struct inode *inode;
FAR struct hostfs_mountpt_s *fs;
FAR struct hostfs_ofile_s *hf;
char path[HOSTFS_MAX_PATH];
FAR char *path;
size_t len;
int ret;
@ -254,12 +278,18 @@ static int hostfs_open(FAR struct file *filep, FAR const char *relpath,
DEBUGASSERT(fs != NULL);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Take the lock */
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Allocate memory for the open file */
@ -274,7 +304,7 @@ static int hostfs_open(FAR struct file *filep, FAR const char *relpath,
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Try to open the file in the host file system */
@ -328,6 +358,10 @@ errout_with_buffer:
errout_with_lock:
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
if (ret == -EINVAL)
{
ret = -EIO;
@ -839,20 +873,27 @@ static int hostfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
{
FAR struct hostfs_mountpt_s *fs;
FAR struct hostfs_dir_s *hdir;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Recover our private data from the inode instance */
fs = mountpt->i_private;
hdir = fs_heap_zalloc(sizeof(struct hostfs_dir_s));
if (hdir == NULL)
{
return -ENOMEM;
ret = -ENOMEM;
goto errout_with_path;
}
/* Take the lock */
@ -865,7 +906,7 @@ static int hostfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host's opendir function */
@ -878,6 +919,7 @@ static int hostfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
*dir = (FAR struct fs_dirent_s *)hdir;
nxmutex_unlock(&fs->fs_lock);
lib_put_tempbuffer(path);
return OK;
errout_with_lock:
@ -885,6 +927,9 @@ errout_with_lock:
errout_with_hdir:
fs_heap_free(hdir);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
@ -1204,13 +1249,19 @@ static int hostfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
static int hostfs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct hostfs_mountpt_s *fs;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1218,18 +1269,21 @@ static int hostfs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host fs to perform the unlink */
ret = host_unlink(path);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
@ -1244,13 +1298,19 @@ static int hostfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode)
{
FAR struct hostfs_mountpt_s *fs;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1258,18 +1318,21 @@ static int hostfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to do the mkdir */
ret = host_mkdir(path, mode);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
@ -1283,13 +1346,19 @@ static int hostfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
int hostfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct hostfs_mountpt_s *fs;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1299,18 +1368,21 @@ int hostfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to do the mkdir */
ret = host_rmdir(path);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
@ -1325,14 +1397,27 @@ int hostfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
FAR const char *newrelpath)
{
FAR struct hostfs_mountpt_s *fs;
char oldpath[HOSTFS_MAX_PATH];
char newpath[HOSTFS_MAX_PATH];
FAR char *oldpath;
FAR char *newpath;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
oldpath = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (oldpath == NULL)
{
return -ENOMEM;
}
newpath = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (newpath == NULL)
{
ret = -ENOMEM;
goto errout_with_oldpath;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1340,7 +1425,7 @@ int hostfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_newpath;
}
/* Append to the host's root directory */
@ -1355,6 +1440,12 @@ int hostfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
ret = host_rename(oldpath, newpath);
nxmutex_unlock(&fs->fs_lock);
errout_with_newpath:
lib_put_tempbuffer(newpath);
errout_with_oldpath:
lib_put_tempbuffer(oldpath);
return ret;
}
@ -1369,13 +1460,19 @@ static int hostfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct hostfs_mountpt_s *fs;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1383,18 +1480,21 @@ static int hostfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to do the stat operation */
ret = host_stat(path, buf);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
@ -1409,13 +1509,19 @@ static int hostfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
FAR const struct stat *buf, int flags)
{
FAR struct hostfs_mountpt_s *fs;
char path[HOSTFS_MAX_PATH];
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
@ -1423,21 +1529,239 @@ static int hostfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
return ret;
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, sizeof(path));
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to do the chstat operation */
ret = host_chstat(path, buf, flags);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
#ifdef CONFIG_FS_LINKS
/****************************************************************************
* Name: hostfs_link
*
* Description: Create a hard link
*
****************************************************************************/
static int hostfs_link(FAR struct inode *mountpt, FAR const char *relpath1,
FAR const char *relpath2)
{
FAR struct hostfs_mountpt_s *fs;
FAR char *path1;
FAR char *path2;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path1 = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path1 == NULL)
{
return -ENOMEM;
}
path2 = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path2 == NULL)
{
ret = -ENOMEM;
goto errout_with_path1;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path2;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath1, path1, HOSTFS_MAX_PATH);
hostfs_mkpath(fs, relpath2, path2, HOSTFS_MAX_PATH);
/* Call the host FS to create the hard link */
ret = host_link(path1, path2);
nxmutex_unlock(&fs->fs_lock);
errout_with_path2:
lib_put_tempbuffer(path2);
errout_with_path1:
lib_put_tempbuffer(path1);
return ret;
}
/****************************************************************************
* Name: hostfs_symlink
*
* Description: Create a symbolic link
*
****************************************************************************/
static int hostfs_symlink(FAR struct inode *mountpt,
FAR const char *path1,
FAR const char *relpath2)
{
FAR struct hostfs_mountpt_s *fs;
FAR char *fullpath2;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
fullpath2 = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (fullpath2 == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Build the full path for the link location (relpath2)
* The target (path1) is used as-is, allowing relative or absolute paths
*/
hostfs_mkpath(fs, relpath2, fullpath2, HOSTFS_MAX_PATH);
/* Call the host FS to create the symbolic link */
ret = host_symlink(path1, fullpath2);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(fullpath2);
return ret;
}
/****************************************************************************
* Name: hostfs_readlink
*
* Description: Read the target of a symbolic link
*
****************************************************************************/
static ssize_t hostfs_readlink(FAR struct inode *mountpt,
FAR const char *relpath,
FAR char *buf, size_t bufsize)
{
FAR struct hostfs_mountpt_s *fs;
FAR char *path;
ssize_t ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to read the symbolic link */
ret = host_readlink(path, buf, bufsize);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
/****************************************************************************
* Name: hostfs_lstat
*
* Description: Return information about a file or directory (don't follow
* symbolic links)
*
****************************************************************************/
static int hostfs_lstat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct hostfs_mountpt_s *fs;
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
path = lib_get_tempbuffer(HOSTFS_MAX_PATH);
if (path == NULL)
{
return -ENOMEM;
}
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Append to the host's root directory */
hostfs_mkpath(fs, relpath, path, HOSTFS_MAX_PATH);
/* Call the host FS to do the lstat operation */
ret = host_lstat(path, buf);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
#endif /* CONFIG_FS_LINKS */
/****************************************************************************
* Public Functions
****************************************************************************/

View file

@ -256,6 +256,13 @@ int host_rename(const char *oldpath, const char *newpath);
int host_stat(const char *path, struct nuttx_stat_s *buf);
int host_chstat(const char *path,
const struct nuttx_stat_s *buf, int flags);
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2);
int host_symlink(const char *target, const char *linkpath);
nuttx_ssize_t host_readlink(const char *path, char *buf,
nuttx_size_t bufsize);
int host_lstat(const char *path, struct nuttx_stat_s *buf);
#endif
#else
int host_open(const char *pathname, int flags, int mode);
int host_close(int fd);
@ -280,6 +287,12 @@ int host_rename(const char *oldpath, const char *newpath);
int host_stat(const char *path, struct stat *buf);
int host_chstat(const char *path,
const struct stat *buf, int flags);
#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2);
int host_symlink(const char *target, const char *linkpath);
ssize_t host_readlink(const char *path, char *buf, size_t bufsize);
int host_lstat(const char *path, struct stat *buf);
#endif
#endif /* __SIM__ */
#endif /* __INCLUDE_NUTTX_FS_HOSTFS_H */