You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
6.2 KiB
414 lines
6.2 KiB
/* |
|
* Copyright (c) 2018 Intel Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#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> |
|
|
|
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_MAX_OPEN_FILES]; |
|
|
|
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_MAX_OPEN_FILES; 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; |
|
|
|
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; |
|
} |
|
|
|
/** |
|
* @brief Open a file. |
|
* |
|
* See IEEE 1003.1 |
|
*/ |
|
int open(const char *name, int flags, ...) |
|
{ |
|
int rc, fd; |
|
struct posix_fs_desc *ptr = NULL; |
|
int zmode = posix_mode_to_zephyr(flags); |
|
|
|
if (zmode < 0) { |
|
return zmode; |
|
} |
|
|
|
fd = z_reserve_fd(); |
|
if (fd < 0) { |
|
return -1; |
|
} |
|
|
|
ptr = posix_fs_alloc_obj(false); |
|
if (ptr == NULL) { |
|
z_free_fd(fd); |
|
errno = EMFILE; |
|
return -1; |
|
} |
|
|
|
fs_file_t_init(&ptr->file); |
|
|
|
rc = fs_open(&ptr->file, name, zmode); |
|
|
|
if (rc < 0) { |
|
posix_fs_free_obj(ptr); |
|
z_free_fd(fd); |
|
errno = -rc; |
|
return -1; |
|
} |
|
|
|
z_finalize_fd(fd, ptr, &fs_fd_op_vtable); |
|
|
|
return fd; |
|
} |
|
|
|
#if !defined(CONFIG_NEWLIB_LIBC) && !defined(CONFIG_PICOLIBC) |
|
FUNC_ALIAS(open, _open, int); |
|
#endif |
|
|
|
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_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; |
|
} |
|
|
|
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; |
|
} |
|
|
|
/** |
|
* @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; |
|
}
|
|
|