| /* |
| * 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_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; |
| } |
| |
| 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; |
| } |
| |
| /** |
| * @brief Truncate file to specified length. |
| * |
| */ |
| int ftruncate(int fd, off_t length) |
| { |
| int rc; |
| struct posix_fs_desc *ptr = NULL; |
| |
| ptr = z_get_fd_obj(fd, NULL, EBADF); |
| if (!ptr) |
| return -1; |
| |
| rc = fs_truncate(&ptr->file, length); |
| if (rc < 0) { |
| errno = -rc; |
| return -1; |
| } |
| |
| return 0; |
| } |