// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include #include #include #include #include "vfs_internal_dirent.hpp" #include "HandleManager.hpp" #include #include // NOTE: It will be removed in later stage #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /** NOTE: This is generic wrapper for newlib syscalls * using current VFS implementation. This implementation * will be removed in next release when disk manager * and new VFS filesystem will be ready */ namespace vfsn::internal { namespace { stdlib::HandleManager gFileHandles; } namespace { auto open_to_fopen_flags(int flags) { flags &= ~0x00010000; // Ignore binary if ((flags & O_RDONLY) || !flags) { return "r"; } else if (flags & (O_WRONLY | O_CREAT | O_TRUNC)) { return "w"; } else if (flags & (O_WRONLY | O_CREAT | O_APPEND)) { return "a"; } else if (flags & O_RDWR) { return "r+"; } else if (flags & (O_RDWR | O_CREAT | O_TRUNC)) { return "w+"; } else if (flags & (O_RDWR | O_CREAT | O_APPEND)) { return "a+"; } else { LOG_ERROR("Unsupported flag %08x", flags); return ""; } } } // namespace } // namespace vfsn::internal namespace vfsn::internal::syscalls { int open(int &_errno_, const char *file, int flags, int mode) { auto fflags = open_to_fopen_flags(flags); if (*fflags == '\0') { _errno_ = EINVAL; return -1; } auto fil = ff_fopen(vfs.relativeToRoot(file).c_str(), fflags); if (!fil) { _errno_ = stdioGET_ERRNO(); return -1; } int hwnd = gFileHandles.setHandle(fil); if (hwnd < 0) { ff_fclose(fil); _errno_ = EMFILE; LOG_ERROR("Unable to find handle %i", hwnd); return -1; } else { _errno_ = 0; return hwnd; } } long close(int &_errno_, int fd) { auto fil = gFileHandles.clearHandle(fd); if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } auto ret = ff_fclose(fil); _errno_ = stdioGET_ERRNO(); return ret; } long write(int &_errno_, int fd, const void *buf, size_t cnt) { if (fd == STDERR_FILENO || fd == STDOUT_FILENO) { log_WriteToDevice((uint8_t *)buf, cnt); return cnt; } auto fil = gFileHandles[fd]; if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } auto ret = ff_fwrite(buf, cnt, 1, fil); _errno_ = stdioGET_ERRNO(); return ret * cnt; } long read(int &_errno_, int fd, void *buf, size_t cnt) { auto fil = gFileHandles[fd]; if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } auto ret = ff_fread(buf, 1, cnt, fil); _errno_ = stdioGET_ERRNO(); return ret * cnt; } off_t lseek(int &_errno_, int fd, off_t pos, int dir) { auto fil = gFileHandles[fd]; if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } auto ret = ff_fseek(fil, pos, dir); if (ret) { _errno_ = stdioGET_ERRNO(); return static_cast(-1); } ret = ff_ftell(fil); if (ret < 0) { _errno_ = stdioGET_ERRNO(); return static_cast(-1); } return ret; } int fstat(int &_errno_, int fd, struct stat *pstat) { auto fil = gFileHandles[fd]; if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } std::memset(pstat, 0, sizeof(*pstat)); pstat->st_ino = fil->ulObjectCluster; pstat->st_size = fil->ulFileSize; pstat->st_dev = 0; pstat->st_mode = S_IFREG | 0666; _errno_ = 0; return 0; } int link(int &_errno_, const char *existing, const char *newLink) { _errno_ = ENOSYS; LOG_ERROR("Syscall %s not supported", __PRETTY_FUNCTION__); return -1; } int unlink(int &_errno_, const char *name) { const auto rel_name = vfs.relativeToRoot(name); auto ret = ff_remove(rel_name.c_str()); if (ret && stdioGET_ERRNO() == EISDIR) ret = ff_deltree(rel_name.c_str(), nullptr, nullptr); _errno_ = stdioGET_ERRNO(); return ret; } int fcntl(int &_errno_, int fd, int cmd, int arg) { _errno_ = ENOSYS; LOG_ERROR("Syscall %s not supported", __PRETTY_FUNCTION__); return -1; } int stat(int &_errno_, const char *file, struct stat *pstat) { FF_Stat_t stat_ff; auto ret = ff_stat(vfs.relativeToRoot(file).c_str(), &stat_ff); if (!ret) { std::memset(pstat, 0, sizeof(*pstat)); pstat->st_ino = stat_ff.st_ino; pstat->st_size = stat_ff.st_size; pstat->st_dev = stat_ff.st_dev; // Linux mode compat if (stat_ff.st_mode == FF_IFREG) pstat->st_mode = S_IFREG | 0666; if (stat_ff.st_mode == FF_IFDIR) pstat->st_mode = S_IFDIR | 0777; } _errno_ = stdioGET_ERRNO(); // NOTE: ff_stdio uses wrong errno NOADDRESS if (_errno_ == EFAULT) _errno_ = ENOENT; return ret; } int chdir(int &_errno_, const char *path) { auto ret = ff_chdir(vfs.relativeToRoot(path).c_str()); _errno_ = stdioGET_ERRNO(); return ret; } char *getcwd(int &_errno_, char *buf, size_t size) { auto cwd = ff_getcwd(buf, size); _errno_ = stdioGET_ERRNO(); return cwd; } int rename(int &_errno_, const char *oldName, const char *newName) { auto ret = ff_rename(vfs.relativeToRoot(oldName).c_str(), vfs.relativeToRoot(newName).c_str(), true); _errno_ = stdioGET_ERRNO(); return ret; } int mkdir(int &_errno_, const char *path, uint32_t mode) { auto ret = ff_mkdir(vfs.relativeToRoot(path).c_str()); _errno_ = stdioGET_ERRNO(); return ret; } DIR *opendir(int &_errno_, const char *dirname) { auto dir = new DIR; dir->dir_data = dirent::diropen(_errno_, vfs.relativeToRoot(dirname).c_str()); if (!dir->dir_data) { delete dir; return nullptr; } dir->position = 0; dir->file_data.d_ino = -1; dir->file_data.d_name[0] = '\0'; return dir; } int closedir(int &_errno_, DIR *dirp) { if (!dirp) { _errno_ = EBADF; return -1; } auto res = dirent::dirclose(_errno_, dirp->dir_data); delete dirp; return res; } struct dirent *readdir(int &_errno_, DIR *dirp) { if (!dirp) { _errno_ = EBADF; return nullptr; } auto olderrno{_errno_}; auto res = dirent::dirnext(_errno_, dirp->dir_data); auto fff = reinterpret_cast(dirp->dir_data->dir_state); if (res < 0) { if (_errno_ == pdFREERTOS_ERRNO_ENMFILE) { _errno_ = olderrno; } return nullptr; } dirp->position += 1; if (strnlen(fff->pcFileName, NAME_MAX) >= sizeof(dirp->file_data.d_name)) { _errno_ = EOVERFLOW; return nullptr; } dirp->file_data.d_ino = fff->xDirectoryEntry.ulObjectCluster; dirp->file_data.d_type = (fff->ucAttributes & FF_FAT_ATTR_DIR) ? DT_DIR : DT_REG; std::strncpy(dirp->file_data.d_name, fff->pcFileName, sizeof(dirp->file_data.d_name)); return &dirp->file_data; } int readdir_r(int &_errno_, DIR *dirp, struct dirent *entry, struct dirent **result) { if (!dirp) { return EBADF; } auto olderrno{_errno_}; auto res = dirent::dirnext(_errno_, dirp->dir_data); auto fff = reinterpret_cast(dirp->dir_data->dir_state); if (res < 0) { res = _errno_; if (_errno_ == pdFREERTOS_ERRNO_ENMFILE) { res = 0; } _errno_ = olderrno; return res; } dirp->position += 1; if (strnlen(fff->pcFileName, NAME_MAX) >= sizeof(entry->d_name)) { return EOVERFLOW; } entry->d_ino = fff->xDirectoryEntry.ulObjectCluster; entry->d_type = (fff->ucAttributes & FF_FAT_ATTR_DIR) ? DT_DIR : DT_REG; std::strncpy(entry->d_name, fff->pcFileName, sizeof(entry->d_name)); *result = entry; return 0; } void rewinddir(int &_errno_, DIR *dirp) { if (!dirp) { return; } dirent::dirreset(_errno_, dirp->dir_data); dirp->position = 0; } void seekdir(int &_errno_, DIR *dirp, long int loc) { if (!dirp || loc < 0) { return; } if (dirp->position > loc) { dirent::dirreset(_errno_, dirp->dir_data); dirp->position = 0; } while ((dirp->position < loc) && (dirent::dirnext(_errno_, dirp->dir_data) >= 0)) { dirp->position += 1; } } long int telldir(int &_errno_, DIR *dirp) { if (!dirp) { return -1; } return dirp->position; } int chmod(int &_errno_, const char *path, mode_t mode) { _errno_ = ENOSYS; LOG_ERROR("Syscall %s not supported", __PRETTY_FUNCTION__); return -1; } int fchmod(int &_errno_, int fd, mode_t mode) { _errno_ = ENOSYS; LOG_ERROR("Syscall %s not supported", __PRETTY_FUNCTION__); return -1; } int fsync(int &_errno_, int fd) { auto fil = gFileHandles[fd]; if (!fil) { LOG_ERROR("Unable to find handle %i", fd); _errno_ = EBADF; return -1; } auto ret = ff_fflush(fil); _errno_ = stdioGET_ERRNO(); return ret; } } // namespace vfsn::internal::syscalls #pragma GCC diagnostic pop