/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include 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); }