blob: c8221a35f955d99c61e40bd5719685c6635cbf7f [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <zephyr/kernel.h>
#include <limits.h>
#include <zephyr/posix/unistd.h>
#include <zephyr/posix/dirent.h>
#include <string.h>
#include <zephyr/sys/fdtable.h>
#include <zephyr/posix/sys/stat.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/fs/fs.h>
int zvfs_fstat(int fd, struct stat *buf);
BUILD_ASSERT(PATH_MAX >= MAX_FILE_NAME, "PATH_MAX is less than MAX_FILE_NAME");
struct posix_fs_desc {
union {
struct fs_file_t file;
struct fs_dir_t dir;
};
bool is_dir;
bool used;
};
static struct posix_fs_desc desc_array[CONFIG_POSIX_OPEN_MAX];
static struct fs_dirent fdirent;
static struct dirent pdirent;
static struct fd_op_vtable fs_fd_op_vtable;
static struct posix_fs_desc *posix_fs_alloc_obj(bool is_dir)
{
int i;
struct posix_fs_desc *ptr = NULL;
unsigned int key = irq_lock();
for (i = 0; i < CONFIG_POSIX_OPEN_MAX; i++) {
if (desc_array[i].used == false) {
ptr = &desc_array[i];
ptr->used = true;
ptr->is_dir = is_dir;
break;
}
}
irq_unlock(key);
return ptr;
}
static inline void posix_fs_free_obj(struct posix_fs_desc *ptr)
{
ptr->used = false;
}
static int posix_mode_to_zephyr(int mf)
{
int mode = (mf & O_CREAT) ? FS_O_CREATE : 0;
mode |= (mf & O_APPEND) ? FS_O_APPEND : 0;
mode |= (mf & O_TRUNC) ? FS_O_TRUNC : 0;
switch (mf & O_ACCMODE) {
case O_RDONLY:
mode |= FS_O_READ;
break;
case O_WRONLY:
mode |= FS_O_WRITE;
break;
case O_RDWR:
mode |= FS_O_RDWR;
break;
default:
break;
}
return mode;
}
int zvfs_open(const char *name, int flags, int mode)
{
int rc, fd;
struct posix_fs_desc *ptr = NULL;
int zmode = posix_mode_to_zephyr(flags);
if (zmode < 0) {
return zmode;
}
fd = zvfs_reserve_fd();
if (fd < 0) {
return -1;
}
ptr = posix_fs_alloc_obj(false);
if (ptr == NULL) {
rc = -EMFILE;
goto out_err;
}
fs_file_t_init(&ptr->file);
if (flags & O_CREAT) {
flags &= ~O_CREAT;
rc = fs_open(&ptr->file, name, FS_O_CREATE | (mode & O_ACCMODE));
if (rc < 0) {
goto out_err;
}
rc = fs_close(&ptr->file);
if (rc < 0) {
goto out_err;
}
}
rc = fs_open(&ptr->file, name, zmode);
if (rc < 0) {
goto out_err;
}
zvfs_finalize_fd(fd, ptr, &fs_fd_op_vtable);
goto out;
out_err:
if (ptr != NULL) {
posix_fs_free_obj(ptr);
}
zvfs_free_fd(fd);
errno = -rc;
return -1;
out:
return fd;
}
static int fs_close_vmeth(void *obj)
{
struct posix_fs_desc *ptr = obj;
int rc;
rc = fs_close(&ptr->file);
posix_fs_free_obj(ptr);
return rc;
}
static int fs_ioctl_vmeth(void *obj, unsigned int request, va_list args)
{
int rc = 0;
struct posix_fs_desc *ptr = obj;
switch (request) {
case ZFD_IOCTL_FSYNC: {
rc = fs_sync(&ptr->file);
break;
}
case ZFD_IOCTL_LSEEK: {
off_t offset;
int whence;
offset = va_arg(args, off_t);
whence = va_arg(args, int);
rc = fs_seek(&ptr->file, offset, whence);
if (rc == 0) {
rc = fs_tell(&ptr->file);
}
break;
}
case ZFD_IOCTL_TRUNCATE: {
off_t length;
length = va_arg(args, off_t);
rc = fs_truncate(&ptr->file, length);
if (rc < 0) {
errno = -rc;
return -1;
}
break;
}
default:
errno = EOPNOTSUPP;
return -1;
}
if (rc < 0) {
errno = -rc;
return -1;
}
return rc;
}
/**
* @brief Write to a file.
*
* See IEEE 1003.1
*/
static ssize_t fs_write_vmeth(void *obj, const void *buffer, size_t count)
{
ssize_t rc;
struct posix_fs_desc *ptr = obj;
rc = fs_write(&ptr->file, buffer, count);
if (rc < 0) {
errno = -rc;
return -1;
}
return rc;
}
/**
* @brief Read from a file.
*
* See IEEE 1003.1
*/
static ssize_t fs_read_vmeth(void *obj, void *buffer, size_t count)
{
ssize_t rc;
struct posix_fs_desc *ptr = obj;
rc = fs_read(&ptr->file, buffer, count);
if (rc < 0) {
errno = -rc;
return -1;
}
return rc;
}
static struct fd_op_vtable fs_fd_op_vtable = {
.read = fs_read_vmeth,
.write = fs_write_vmeth,
.close = fs_close_vmeth,
.ioctl = fs_ioctl_vmeth,
};
/**
* @brief Open a directory stream.
*
* See IEEE 1003.1
*/
DIR *opendir(const char *dirname)
{
int rc;
struct posix_fs_desc *ptr;
ptr = posix_fs_alloc_obj(true);
if (ptr == NULL) {
errno = EMFILE;
return NULL;
}
fs_dir_t_init(&ptr->dir);
rc = fs_opendir(&ptr->dir, dirname);
if (rc < 0) {
posix_fs_free_obj(ptr);
errno = -rc;
return NULL;
}
return ptr;
}
/**
* @brief Close a directory stream.
*
* See IEEE 1003.1
*/
int closedir(DIR *dirp)
{
int rc;
struct posix_fs_desc *ptr = dirp;
if (dirp == NULL) {
errno = EBADF;
return -1;
}
rc = fs_closedir(&ptr->dir);
posix_fs_free_obj(ptr);
if (rc < 0) {
errno = -rc;
return -1;
}
return 0;
}
/**
* @brief Read a directory.
*
* See IEEE 1003.1
*/
struct dirent *readdir(DIR *dirp)
{
int rc;
struct posix_fs_desc *ptr = dirp;
if (dirp == NULL) {
errno = EBADF;
return NULL;
}
rc = fs_readdir(&ptr->dir, &fdirent);
if (rc < 0) {
errno = -rc;
return NULL;
}
if (fdirent.name[0] == 0) {
/* assume end-of-dir, leave errno untouched */
return NULL;
}
rc = strlen(fdirent.name);
rc = (rc < MAX_FILE_NAME) ? rc : (MAX_FILE_NAME - 1);
(void)memcpy(pdirent.d_name, fdirent.name, rc);
/* Make sure the name is NULL terminated */
pdirent.d_name[rc] = '\0';
return &pdirent;
}
#ifdef CONFIG_POSIX_FILE_SYSTEM_R
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
struct dirent *dir;
errno = 0;
dir = readdir(dirp);
if (dir == NULL) {
int error = errno;
if (error != 0) {
if (result != NULL) {
*result = NULL;
}
return 0;
} else {
return error;
}
}
if (entry != NULL) {
memcpy(entry, dir, sizeof(struct dirent));
}
if (result != NULL) {
*result = entry;
}
return 0;
}
#endif /* CONFIG_POSIX_FILE_SYSTEM_R */
/**
* @brief Rename a file.
*
* See IEEE 1003.1
*/
int rename(const char *old, const char *new)
{
int rc;
rc = fs_rename(old, new);
if (rc < 0) {
errno = -rc;
return -1;
}
return 0;
}
/**
* @brief Remove a directory entry.
*
* See IEEE 1003.1
*/
int unlink(const char *path)
{
int rc;
rc = fs_unlink(path);
if (rc < 0) {
errno = -rc;
return -1;
}
return 0;
}
/**
* @brief Get file status.
*
* See IEEE 1003.1
*/
int stat(const char *path, struct stat *buf)
{
int rc;
struct fs_statvfs stat_vfs;
struct fs_dirent stat_file;
if (buf == NULL) {
errno = EBADF;
return -1;
}
rc = fs_statvfs(path, &stat_vfs);
if (rc < 0) {
errno = -rc;
return -1;
}
rc = fs_stat(path, &stat_file);
if (rc < 0) {
errno = -rc;
return -1;
}
memset(buf, 0, sizeof(struct stat));
switch (stat_file.type) {
case FS_DIR_ENTRY_FILE:
buf->st_mode = S_IFREG;
break;
case FS_DIR_ENTRY_DIR:
buf->st_mode = S_IFDIR;
break;
default:
errno = EIO;
return -1;
}
buf->st_size = stat_file.size;
buf->st_blksize = stat_vfs.f_bsize;
/*
* This is a best effort guess, as this information is not provided
* by the fs_stat function.
*/
buf->st_blocks = (stat_file.size + stat_vfs.f_bsize - 1) / stat_vfs.f_bsize;
return 0;
}
/**
* @brief Make a directory.
*
* See IEEE 1003.1
*/
int mkdir(const char *path, mode_t mode)
{
int rc;
ARG_UNUSED(mode);
rc = fs_mkdir(path);
if (rc < 0) {
errno = -rc;
return -1;
}
return 0;
}
int fstat(int fildes, struct stat *buf)
{
return zvfs_fstat(fildes, buf);
}
#ifdef CONFIG_POSIX_FILE_SYSTEM_ALIAS_FSTAT
FUNC_ALIAS(fstat, _fstat, int);
#endif
/**
* @brief Remove a directory.
*
* See IEEE 1003.1
*/
int rmdir(const char *path)
{
return unlink(path);
}