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

Implement link(), symlink(), readlink() and lstat() in rpmsgfs.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
This commit is contained in:
zhengyu16 2025-12-02 14:13:28 +08:00 committed by GUIDINGLI
parent 86193c95ca
commit 41ec966941
4 changed files with 522 additions and 49 deletions

View file

@ -147,6 +147,21 @@ static int rpmsgfs_chstat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR const struct stat *buf, int flags);
#ifdef CONFIG_FS_LINKS
static int rpmsgfs_link(FAR struct inode *mountpt,
FAR const char *relpath1,
FAR const char *relpath2);
static int rpmsgfs_symlink(FAR struct inode *mountpt,
FAR const char *target,
FAR const char *relpath);
static ssize_t rpmsgfs_readlink(FAR struct inode *mountpt,
FAR const char *relpath,
FAR char *buf, size_t bufsize);
static int rpmsgfs_lstat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct stat *buf);
#endif
/****************************************************************************
* Public Data
****************************************************************************/
@ -190,6 +205,16 @@ const struct mountpt_operations g_rpmsgfs_operations =
rpmsgfs_rename, /* rename */
rpmsgfs_stat, /* stat */
rpmsgfs_chstat, /* chstat */
NULL, /* syncfs */
NULL, /* ioctldir */
NULL, /* permission */
#ifdef CONFIG_FS_LINKS
rpmsgfs_link, /* link */
rpmsgfs_symlink, /* symlink */
rpmsgfs_readlink, /* readlink */
rpmsgfs_lstat, /* lstat */
#endif
};
/****************************************************************************
@ -1551,3 +1576,217 @@ static int rpmsgfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
lib_put_pathbuffer(path);
return ret;
}
#ifdef CONFIG_FS_LINKS
/****************************************************************************
* Name: rpmsgfs_link
*
* Description: Create a hard link
*
****************************************************************************/
static int rpmsgfs_link(FAR struct inode *mountpt, FAR const char *relpath1,
FAR const char *relpath2)
{
FAR struct rpmsgfs_mountpt_s *fs;
FAR char *path1;
FAR char *path2;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
path1 = lib_get_tempbuffer(PATH_MAX);
if (path1 == NULL)
{
return -ENOMEM;
}
path2 = lib_get_tempbuffer(PATH_MAX);
if (path2 == NULL)
{
ret = -ENOMEM;
goto errout_with_path1;
}
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path2;
}
/* Append to the host's root directory */
rpmsgfs_mkpath(fs, relpath1, path1, PATH_MAX);
rpmsgfs_mkpath(fs, relpath2, path2, PATH_MAX);
/* Call the host FS to do the link operation */
ret = rpmsgfs_client_link(fs->handle, path1, path2);
nxmutex_unlock(&fs->fs_lock);
errout_with_path2:
lib_put_tempbuffer(path2);
errout_with_path1:
lib_put_tempbuffer(path1);
return ret;
}
/****************************************************************************
* Name: rpmsgfs_symlink
*
* Description: Create a symbolic link
*
****************************************************************************/
static int rpmsgfs_symlink(FAR struct inode *mountpt, FAR const char *target,
FAR const char *relpath)
{
FAR struct rpmsgfs_mountpt_s *fs;
FAR char *linkpath;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
linkpath = lib_get_tempbuffer(PATH_MAX);
if (linkpath == NULL)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Append to the host's root directory.
* Note: target is stored as-is (it's a path in the remote system),
* only linkpath needs to be converted to host path.
*/
rpmsgfs_mkpath(fs, relpath, linkpath, PATH_MAX);
/* Call the host FS to do the symlink operation */
ret = rpmsgfs_client_symlink(fs->handle, target, linkpath);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(linkpath);
return ret;
}
/****************************************************************************
* Name: rpmsgfs_readlink
*
* Description: Read the target of a symbolic link
*
****************************************************************************/
static ssize_t rpmsgfs_readlink(FAR struct inode *mountpt,
FAR const char *relpath, FAR char *buf,
size_t bufsize)
{
FAR struct rpmsgfs_mountpt_s *fs;
FAR char *path;
ssize_t ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
path = lib_get_tempbuffer(PATH_MAX);
if (path == NULL)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Append to the host's root directory */
rpmsgfs_mkpath(fs, relpath, path, PATH_MAX);
/* Call the host FS to do the readlink operation */
ret = rpmsgfs_client_readlink(fs->handle, path, buf, bufsize);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
/****************************************************************************
* Name: rpmsgfs_lstat
*
* Description: Return information about a file or directory (without
* following symlinks)
*
****************************************************************************/
static int rpmsgfs_lstat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct rpmsgfs_mountpt_s *fs;
FAR char *path;
int ret;
/* Sanity checks */
DEBUGASSERT(mountpt && mountpt->i_private);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
path = lib_get_tempbuffer(PATH_MAX);
if (path == NULL)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->fs_lock);
if (ret < 0)
{
goto errout_with_path;
}
/* Append to the host's root directory */
rpmsgfs_mkpath(fs, relpath, path, PATH_MAX);
/* Call the host FS to do the lstat operation */
ret = rpmsgfs_client_lstat(fs->handle, path, buf);
nxmutex_unlock(&fs->fs_lock);
errout_with_path:
lib_put_tempbuffer(path);
return ret;
}
#endif

View file

@ -61,6 +61,10 @@
#define RPMSGFS_STAT 20
#define RPMSGFS_FCHSTAT 21
#define RPMSGFS_CHSTAT 22
#define RPMSGFS_SYMLINK 23
#define RPMSGFS_READLINK 24
#define RPMSGFS_LINK 25
#define RPMSGFS_LSTAT 26
/****************************************************************************
* Public Types
@ -202,6 +206,10 @@ begin_packed_struct struct rpmsgfs_mkdir_s
#define rpmsgfs_rmdir_s rpmsgfs_opendir_s
#define rpmsgfs_rename_s rpmsgfs_opendir_s
#define rpmsgfs_stat_s rpmsgfs_fstat_s
#define rpmsgfs_lstat_s rpmsgfs_fstat_s
#define rpmsgfs_symlink_s rpmsgfs_opendir_s
#define rpmsgfs_readlink_s rpmsgfs_opendir_s
#define rpmsgfs_link_s rpmsgfs_opendir_s
begin_packed_struct struct rpmsgfs_fchstat_s
{
@ -258,6 +266,16 @@ int rpmsgfs_client_stat(FAR void *handle, FAR const char *path,
FAR struct stat *buf);
int rpmsgfs_client_chstat(FAR void *handle, FAR const char *path,
FAR const struct stat *buf, int flags);
#ifdef CONFIG_FS_LINKS
int rpmsgfs_client_symlink(FAR void *handle, FAR const char *target,
FAR const char *linkpath);
ssize_t rpmsgfs_client_readlink(FAR void *handle, FAR const char *pathname,
FAR char *buf, size_t bufsize);
int rpmsgfs_client_link(FAR void *handle, FAR const char *path1,
FAR const char *path2);
int rpmsgfs_client_lstat(FAR void *handle, FAR const char *path,
FAR struct stat *buf);
#endif
/****************************************************************************
* Public Function Prototypes

View file

@ -83,6 +83,11 @@ static int rpmsgfs_stat_handler(FAR struct rpmsg_endpoint *ept,
static int rpmsgfs_init_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
#ifdef CONFIG_FS_LINKS
static int rpmsgfs_readlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
#endif
static void rpmsgfs_device_created(struct rpmsg_device *rdev,
FAR void *priv_);
static void rpmsgfs_device_destroy(struct rpmsg_device *rdev,
@ -124,6 +129,12 @@ static const rpmsg_ept_cb g_rpmsgfs_handler[] =
[RPMSGFS_STAT] = rpmsgfs_stat_handler,
[RPMSGFS_FCHSTAT] = rpmsgfs_default_handler,
[RPMSGFS_CHSTAT] = rpmsgfs_default_handler,
#ifdef CONFIG_FS_LINKS
[RPMSGFS_SYMLINK] = rpmsgfs_default_handler,
[RPMSGFS_READLINK] = rpmsgfs_readlink_handler,
[RPMSGFS_LINK] = rpmsgfs_default_handler,
[RPMSGFS_LSTAT] = rpmsgfs_stat_handler,
#endif
};
/****************************************************************************
@ -289,6 +300,27 @@ static int rpmsgfs_init_handler(FAR struct rpmsg_endpoint *ept,
rpmsg_post(&ept_priv->ept, &ept_priv->wait);
return 0;
}
#ifdef CONFIG_FS_LINKS
static int rpmsgfs_readlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsgfs_header_s *header = data;
FAR struct rpmsgfs_cookie_s *cookie =
(struct rpmsgfs_cookie_s *)(uintptr_t)header->cookie;
FAR struct rpmsgfs_readlink_s *rsp = data;
cookie->result = header->result;
if (cookie->result > 0)
{
memcpy(cookie->data, rsp->pathname, cookie->result);
}
rpmsg_post(ept, &cookie->sem);
return 0;
}
#endif
static FAR void *rpmsgfs_get_tx_payload_buffer(FAR struct rpmsgfs_s *priv,
FAR uint32_t *len)
@ -1029,3 +1061,109 @@ int rpmsgfs_client_chstat(FAR void *handle, FAR const char *path,
return rpmsgfs_send_recv(priv, RPMSGFS_CHSTAT, false,
(struct rpmsgfs_header_s *)msg, len, NULL);
}
int rpmsgfs_client_symlink(FAR void *handle, FAR const char *target,
FAR const char *linkpath)
{
FAR struct rpmsgfs_s *priv = handle;
FAR struct rpmsgfs_symlink_s *msg;
size_t len1;
size_t len2;
size_t len;
uint32_t space;
len1 = strlen(target) + 1;
len2 = strlen(linkpath) + 1;
len = sizeof(*msg) + len1 + len2;
msg = rpmsgfs_get_tx_payload_buffer(priv, &space);
if (!msg)
{
return -ENOMEM;
}
DEBUGASSERT(len <= space);
memcpy(msg->pathname, target, len1);
memcpy(msg->pathname + len1, linkpath, len2);
return rpmsgfs_send_recv(priv, RPMSGFS_SYMLINK, false,
(struct rpmsgfs_header_s *)msg, len, NULL);
}
ssize_t rpmsgfs_client_readlink(FAR void *handle, FAR const char *pathname,
FAR char *buf, size_t bufsize)
{
FAR struct rpmsgfs_s *priv = handle;
FAR struct rpmsgfs_readlink_s *msg;
uint32_t space;
size_t len;
len = sizeof(*msg) + strlen(pathname) + 1;
msg = rpmsgfs_get_tx_payload_buffer(priv, &space);
if (!msg)
{
return -ENOMEM;
}
DEBUGASSERT(len <= space);
strlcpy(msg->pathname, pathname, space - sizeof(*msg));
return rpmsgfs_send_recv(priv, RPMSGFS_READLINK, false,
(struct rpmsgfs_header_s *)msg, len, buf);
}
int rpmsgfs_client_link(FAR void *handle, FAR const char *path1,
FAR const char *path2)
{
FAR struct rpmsgfs_s *priv = handle;
FAR struct rpmsgfs_link_s *msg;
size_t len1;
size_t len2;
size_t len;
uint32_t space;
len1 = strlen(path1) + 1;
len2 = strlen(path2) + 1;
len = sizeof(*msg) + len1 + len2;
msg = rpmsgfs_get_tx_payload_buffer(priv, &space);
if (!msg)
{
return -ENOMEM;
}
DEBUGASSERT(len <= space);
memcpy(msg->pathname, path1, len1);
memcpy(msg->pathname + len1, path2, len2);
return rpmsgfs_send_recv(priv, RPMSGFS_LINK, false,
(struct rpmsgfs_header_s *)msg, len, NULL);
}
int rpmsgfs_client_lstat(FAR void *handle, FAR const char *path,
FAR struct stat *buf)
{
FAR struct rpmsgfs_s *priv = handle;
FAR struct rpmsgfs_lstat_s *msg;
uint32_t space;
size_t len;
len = sizeof(*msg) + strlen(path) + 1;
msg = rpmsgfs_get_tx_payload_buffer(priv, &space);
if (!msg)
{
return -ENOMEM;
}
DEBUGASSERT(len <= space);
strlcpy(msg->pathname, path, space - sizeof(*msg));
return rpmsgfs_send_recv(priv, RPMSGFS_LSTAT, false,
(struct rpmsgfs_header_s *)msg, len, buf);
}

View file

@ -126,7 +126,20 @@ static int rpmsgfs_fchstat_handler(FAR struct rpmsg_endpoint *ept,
static int rpmsgfs_chstat_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
#ifdef CONFIG_FS_LINKS
static int rpmsgfs_symlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int rpmsgfs_readlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int rpmsgfs_link_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int rpmsgfs_lstat_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
#endif
static bool rpmsgfs_ns_match(FAR struct rpmsg_device *rdev,
FAR void *priv_, FAR const char *name,
uint32_t dest);
@ -165,6 +178,12 @@ static const rpmsg_ept_cb g_rpmsgfs_handler[] =
[RPMSGFS_STAT] = rpmsgfs_stat_handler,
[RPMSGFS_FCHSTAT] = rpmsgfs_fchstat_handler,
[RPMSGFS_CHSTAT] = rpmsgfs_chstat_handler,
#ifdef CONFIG_FS_LINKS
[RPMSGFS_SYMLINK] = rpmsgfs_symlink_handler,
[RPMSGFS_READLINK] = rpmsgfs_readlink_handler,
[RPMSGFS_LINK] = rpmsgfs_link_handler,
[RPMSGFS_LSTAT] = rpmsgfs_lstat_handler,
#endif
};
/****************************************************************************
@ -313,6 +332,48 @@ static FAR void *rpmsgfs_get_dir(
return dir;
}
static void rpmsgfs_get_statbuf(FAR struct stat *st,
FAR struct rpmsgfs_stat_priv_s *rst)
{
st->st_dev = rst->dev;
st->st_ino = rst->ino;
st->st_mode = rst->mode;
st->st_nlink = rst->nlink;
st->st_uid = rst->uid;
st->st_gid = rst->gid;
st->st_rdev = rst->rdev;
st->st_size = rst->size;
st->st_atim.tv_sec = rst->atim_sec;
st->st_atim.tv_nsec = rst->atim_nsec;
st->st_mtim.tv_sec = rst->mtim_sec;
st->st_mtim.tv_nsec = rst->mtim_nsec;
st->st_ctim.tv_sec = rst->ctim_sec;
st->st_ctim.tv_nsec = rst->ctim_nsec;
st->st_blksize = rst->blksize;
st->st_blocks = rst->blocks;
}
static void rpmsgfs_set_statbuf(FAR struct rpmsgfs_stat_priv_s *rst,
FAR const struct stat *st)
{
rst->dev = st->st_dev;
rst->ino = st->st_ino;
rst->mode = st->st_mode;
rst->nlink = st->st_nlink;
rst->uid = st->st_uid;
rst->gid = st->st_gid;
rst->rdev = st->st_rdev;
rst->size = st->st_size;
rst->atim_sec = st->st_atim.tv_sec;
rst->atim_nsec = st->st_atim.tv_nsec;
rst->mtim_sec = st->st_mtim.tv_sec;
rst->mtim_nsec = st->st_mtim.tv_nsec;
rst->ctim_sec = st->st_ctim.tv_sec;
rst->ctim_nsec = st->st_ctim.tv_nsec;
rst->blksize = st->st_blksize;
rst->blocks = st->st_blocks;
}
static int rpmsgfs_open_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
@ -538,22 +599,7 @@ static int rpmsgfs_fstat_handler(FAR struct rpmsg_endpoint *ept,
ret = file_fstat(filep, &buf);
if (ret >= 0)
{
msg->buf.dev = buf.st_dev;
msg->buf.ino = buf.st_ino;
msg->buf.mode = buf.st_mode;
msg->buf.nlink = buf.st_nlink;
msg->buf.uid = buf.st_uid;
msg->buf.gid = buf.st_gid;
msg->buf.rdev = buf.st_rdev;
msg->buf.size = buf.st_size;
msg->buf.atim_sec = buf.st_atim.tv_sec;
msg->buf.atim_nsec = buf.st_atim.tv_nsec;
msg->buf.mtim_sec = buf.st_mtim.tv_sec;
msg->buf.mtim_nsec = buf.st_mtim.tv_nsec;
msg->buf.ctim_sec = buf.st_ctim.tv_sec;
msg->buf.ctim_nsec = buf.st_ctim.tv_nsec;
msg->buf.blksize = buf.st_blksize;
msg->buf.blocks = buf.st_blocks;
rpmsgfs_set_statbuf(&msg->buf, &buf);
}
}
@ -760,22 +806,7 @@ static int rpmsgfs_stat_handler(FAR struct rpmsg_endpoint *ept,
ret = nx_stat(msg->pathname, &buf, 1);
if (ret >= 0)
{
msg->buf.dev = buf.st_dev;
msg->buf.ino = buf.st_ino;
msg->buf.mode = buf.st_mode;
msg->buf.nlink = buf.st_nlink;
msg->buf.uid = buf.st_uid;
msg->buf.gid = buf.st_gid;
msg->buf.rdev = buf.st_rdev;
msg->buf.size = buf.st_size;
msg->buf.atim_sec = buf.st_atim.tv_sec;
msg->buf.atim_nsec = buf.st_atim.tv_nsec;
msg->buf.mtim_sec = buf.st_mtim.tv_sec;
msg->buf.mtim_nsec = buf.st_mtim.tv_nsec;
msg->buf.ctim_sec = buf.st_ctim.tv_sec;
msg->buf.ctim_nsec = buf.st_ctim.tv_nsec;
msg->buf.blksize = buf.st_blksize;
msg->buf.blocks = buf.st_blocks;
rpmsgfs_set_statbuf(&msg->buf, &buf);
}
msg->header.result = ret;
@ -794,22 +825,7 @@ static int rpmsgfs_fchstat_handler(FAR struct rpmsg_endpoint *ept,
filep = rpmsgfs_get_file(priv, msg->fd);
if (filep != NULL)
{
buf.st_dev = msg->buf.dev;
buf.st_ino = msg->buf.ino;
buf.st_mode = msg->buf.mode;
buf.st_nlink = msg->buf.nlink ;
buf.st_uid = msg->buf.uid ;
buf.st_gid = msg->buf.gid;
buf.st_rdev = msg->buf.rdev;
buf.st_size = msg->buf.size;
buf.st_atim.tv_sec = msg->buf.atim_sec;
buf.st_atim.tv_nsec = msg->buf.atim_nsec;
buf.st_mtim.tv_sec = msg->buf.mtim_sec;
buf.st_mtim.tv_nsec = msg->buf.mtim_nsec;
buf.st_ctim.tv_sec = msg->buf.ctim_sec;
buf.st_ctim.tv_nsec = msg->buf.ctim_nsec;
buf.st_blksize = msg->buf.blksize;
buf.st_blocks = msg->buf.blocks;
rpmsgfs_get_statbuf(&buf, &msg->buf);
ret = file_fchstat(filep, &buf, msg->flags);
}
@ -883,6 +899,68 @@ out:
return rpmsg_send(ept, msg, sizeof(*msg));
}
#ifdef CONFIG_FS_LINKS
static int rpmsgfs_symlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsgfs_symlink_s *msg = data;
FAR const char *target = msg->pathname;
FAR const char *linkpath = target + strlen(target) + 1;
int ret;
ret = symlink(target, linkpath);
msg->header.result = ret ? -get_errno() : 0;
return rpmsg_send(ept, msg, sizeof(*msg));
}
static int rpmsgfs_readlink_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsgfs_readlink_s *msg = data;
ssize_t ret;
uint32_t space;
space = rpmsg_get_tx_buffer_size(ept);
ret = readlink(msg->pathname, msg->pathname, space - sizeof(*msg));
msg->header.result = ret < 0 ? -get_errno() : ret;
return rpmsg_send(ept, msg, sizeof(*msg) + (ret > 0 ? ret : 0));
}
static int rpmsgfs_link_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsgfs_link_s *msg = data;
FAR const char *path1 = msg->pathname;
FAR const char *path2 = path1 + strlen(path1) + 1;
int ret;
ret = link(path1, path2);
msg->header.result = ret ? -get_errno() : 0;
return rpmsg_send(ept, msg, sizeof(*msg));
}
static int rpmsgfs_lstat_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsgfs_lstat_s *msg = data;
struct stat buf;
int ret;
ret = nx_stat(msg->pathname, &buf, 0);
if (ret >= 0)
{
rpmsgfs_set_statbuf(&msg->buf, &buf);
}
msg->header.result = ret;
return rpmsg_send(ept, msg, sizeof(*msg));
}
#endif
static bool rpmsgfs_ns_match(FAR struct rpmsg_device *rdev,
FAR void *priv_, FAR const char *name,
uint32_t dest)