ChibiOS/os/sb/host/sbposix.c

380 lines
9.8 KiB
C

/*
ChibiOS - Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,
2015,2016,2017,2018,2019,2020,2021 Giovanni Di Sirio.
This file is part of ChibiOS.
ChibiOS 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 version 3 of the License.
ChibiOS is distributed in the hope that it will be useful,
but 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, see <http://www.gnu.org/licenses/>.
*/
/**
* @file sb/host/sbposix.c
* @brief ARM SandBox host Posix API code.
*
* @addtogroup ARM_SANDBOX_HOSTAPI
* @{
*/
#include "sb.h"
#if (SB_CFG_ENABLE_VFS == TRUE) || defined(__DOXYGEN__)
#include <dirent.h>
/*===========================================================================*/
/* Module local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Module exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local types. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local functions. */
/*===========================================================================*/
static msg_t create_descriptor(sb_ioblock_t *iop,
vfs_node_c *np) {
unsigned fd;
for (fd = 0U; fd < SB_CFG_FD_NUM; fd++) {
if (iop->vfs_nodes[fd] == NULL) {
iop->vfs_nodes[fd] = np;
return (msg_t)fd;
}
}
return CH_RET_EMFILE;
}
/*===========================================================================*/
/* Module exported functions. */
/*===========================================================================*/
int sb_posix_stat(sb_class_t *sbp, const char *path, struct stat *statbuf) {
msg_t ret;
vfs_stat_t vstat;
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
if (!sb_is_valid_write_range(sbp, (void *)statbuf, sizeof (struct stat))) {
return CH_RET_EFAULT;
}
ret = (int)vfsDrvStat(sbp->config->vfs_driver, path, &vstat);
if (!CH_RET_IS_ERROR(ret)) {
memset((void *)statbuf, 0, sizeof (struct stat));
statbuf->st_mode = (mode_t)vstat.mode;
statbuf->st_size = (off_t)vstat.size;
statbuf->st_nlink = 1;
}
return ret;
}
int sb_posix_open(sb_class_t *sbp, const char *path, int flags) {
vfs_node_c *np = NULL;
msg_t ret;
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
do {
ret = vfsDrvOpen(sbp->config->vfs_driver, path, (unsigned)flags, &np);
CH_BREAK_ON_ERROR(ret);
ret = create_descriptor(&sbp->io, np);
CH_BREAK_ON_ERROR(ret);
return (int)ret;
} while (false);
if (np != NULL) {
vfsClose(np);
}
return (int)ret;
}
int sb_posix_close(sb_class_t *sbp, int fd) {
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
vfsClose(sbp->io.vfs_nodes[fd]);
sbp->io.vfs_nodes[fd] = NULL;
return CH_RET_SUCCESS;
}
int sb_posix_dup(sb_class_t *sbp, int fd) {
vfs_node_c *np;
msg_t ret;
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
/* Node associated to the existing file descriptor.*/
np = sbp->io.vfs_nodes[fd];
/* Adding the same node to the new descriptor with increased reference
counter.*/
ret = create_descriptor(&sbp->io, (vfs_node_c *)roAddRef(np));
if (CH_RET_IS_ERROR(ret)) {
/* In case of error removing the added reference.*/
vfsClose(np);
}
return (int)ret;
}
int sb_posix_dup2(sb_class_t *sbp, int oldfd, int newfd) {
if (!sb_is_existing_descriptor(&sbp->io, oldfd)) {
return CH_RET_EBADF;
}
if (!sb_is_valid_descriptor(oldfd)) {
return CH_RET_EBADF;
}
if (oldfd == newfd) {
return CH_RET_EINVAL;
}
if (sbp->io.vfs_nodes[newfd] != NULL) {
vfsClose(sbp->io.vfs_nodes[newfd]);
}
sbp->io.vfs_nodes[newfd] = (vfs_node_c *)roAddRef(sbp->io.vfs_nodes[oldfd]);
return (int)newfd;
}
int sb_posix_fstat(sb_class_t *sbp, int fd, struct stat *statbuf) {
msg_t ret;
vfs_stat_t vstat;
if (!sb_is_valid_write_range(sbp, (void *)statbuf, sizeof (struct stat))) {
return CH_RET_EFAULT;
}
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
ret = vfsGetNodeStat(sbp->io.vfs_nodes[fd], &vstat);
if (!CH_RET_IS_ERROR(ret)) {
memset((void *)statbuf, 0, sizeof (struct stat));
statbuf->st_mode = (mode_t)vstat.mode;
statbuf->st_size = (off_t)vstat.size;
statbuf->st_nlink = 1;
}
return ret;
}
ssize_t sb_posix_read(sb_class_t *sbp, int fd, void *buf, size_t count) {
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
if (VFS_MODE_S_ISDIR(sbp->io.vfs_nodes[fd]->mode)) {
return CH_RET_EISDIR;
}
if (count == (size_t)0) {
return 0;
}
if (!sb_is_valid_write_range(sbp, buf, count)) {
return CH_RET_EFAULT;
}
return vfsReadFile((vfs_file_node_c *)sbp->io.vfs_nodes[fd], buf, count);
}
ssize_t sb_posix_write(sb_class_t *sbp, int fd, const void *buf, size_t count) {
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
if (VFS_MODE_S_ISDIR(sbp->io.vfs_nodes[fd]->mode)) {
return CH_RET_EISDIR;
}
if (count == (size_t)0) {
return 0;
}
if (!sb_is_valid_read_range(sbp, buf, count)) {
return CH_RET_EFAULT;
}
return vfsWriteFile((vfs_file_node_c *)sbp->io.vfs_nodes[fd], buf, count);
}
off_t sb_posix_lseek(sb_class_t *sbp, int fd, off_t offset, int whence) {
if ((whence != SEEK_SET) || (whence == SEEK_CUR) || (whence != SEEK_END)) {
return CH_RET_EINVAL;
}
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return CH_RET_EBADF;
}
if (VFS_MODE_S_ISDIR(sbp->io.vfs_nodes[fd]->mode)) {
return CH_RET_EISDIR;
}
if (!VFS_MODE_S_ISREG(sbp->io.vfs_nodes[fd]->mode)) {
return CH_RET_ESPIPE;
}
return vfsSetFilePosition((struct vfs_file_node *)sbp->io.vfs_nodes[fd],
offset,
whence);;
}
ssize_t sb_posix_getdents(sb_class_t *sbp, int fd, void *buf, size_t count) {
vfs_shared_buffer_t *shbuf;
vfs_direntry_info_t *dip;
msg_t ret;
if (!sb_is_valid_write_range(sbp, buf, count)) {
return (ssize_t)CH_RET_EFAULT;
}
if (!sb_is_existing_descriptor(&sbp->io, fd)) {
return (ssize_t)CH_RET_EBADF;
}
if (!VFS_MODE_S_ISDIR(sbp->io.vfs_nodes[fd]->mode)) {
return (ssize_t)CH_RET_ENOTDIR;
}
shbuf = vfs_buffer_take_wait();
dip = (vfs_direntry_info_t *)(void *)shbuf->buf;
do {
size_t n;
struct dirent *dep = (struct dirent *)buf;
ret = vfsReadDirectoryNext((vfs_directory_node_c *)sbp->io.vfs_nodes[fd], dip);
if (ret <= 0) {
/* Note, zero means no more directory entries available.*/
break;
}
n = sizeof (struct dirent) + strlen(dip->name) + (size_t)1;
if (count < n) {
ret = CH_RET_EINVAL;
break;
}
/* Copying data from VFS structure to the Posix one.*/
dep->d_ino = (ino_t)1; /* TODO */
dep->d_reclen = n;
dep->d_type = IFTODT(sbp->io.vfs_nodes[fd]->mode);
strcpy(dep->d_name, dip->name);
ret = (msg_t)n;
} while (false);
vfs_buffer_release(shbuf);
return (ssize_t)ret;
}
int sb_posix_chdir(sb_class_t *sbp, const char *path) {
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
return (int)vfsDrvChangeCurrentDirectory(sbp->config->vfs_driver, path);
}
int sb_posix_getcwd(sb_class_t *sbp, char *buf, size_t size) {
if (!sb_is_valid_write_range(sbp, buf, size)) {
return CH_RET_EFAULT;
}
/* Note, it does not return a pointer to the buffer as required by Posix,
this has to be handled on the user-side library.*/
return vfsDrvGetCurrentDirectory(sbp->config->vfs_driver, buf, size);
}
int sb_posix_unlink(sb_class_t *sbp, const char *path) {
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
return (int)vfsDrvUnlink(sbp->config->vfs_driver, path);
}
int sb_posix_rename(sb_class_t *sbp, const char *oldpath, const char *newpath) {
if (sb_check_string(sbp, (void *)oldpath, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
if (sb_check_string(sbp, (void *)newpath, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
return (int)vfsDrvRename(sbp->config->vfs_driver, oldpath, newpath);
}
int sb_posix_mkdir(sb_class_t *sbp, const char *path, mode_t mode) {
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
return (int)vfsDrvMkdir(sbp->config->vfs_driver, path, (vfs_mode_t)mode);
}
int sb_posix_rmdir(sb_class_t *sbp, const char *path) {
if (sb_check_string(sbp, (void *)path, VFS_CFG_PATHLEN_MAX + 1) == (size_t)0) {
return CH_RET_EFAULT;
}
return (int)vfsDrvRmdir(sbp->config->vfs_driver, path);
}
#endif
/** @} */