diff --git a/include/coreinit/filesystem.h b/include/coreinit/filesystem.h index 2ef8850..5453745 100644 --- a/include/coreinit/filesystem.h +++ b/include/coreinit/filesystem.h @@ -11,9 +11,6 @@ * want to run in parallel. You must ensure the previous filesystem command * has been completed before reusing the same FSCmdBlock, you do not need to * reinitialise an FSCmdBlock before reusing it. - * - * Calling fsDevInit initializes the stdlib devoptab, allowing for standard - * file IO. * @{ */ @@ -196,12 +193,6 @@ struct FSMountSource }; CHECK_SIZE(FSMountSource, 0x300); -FSStatus -fsDevInit(); - -FSStatus -fsDevExit(); - void FSInit(); diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 99bf979..f192320 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -4,5 +4,6 @@ project(libraries C) add_subdirectory(libdefaultheap) add_subdirectory(libgfd) add_subdirectory(libwhb) +add_subdirectory(wutdevoptab) add_subdirectory(wutnewlib) add_subdirectory(wutstdc++) diff --git a/libraries/wutdevoptab/CMakeLists.txt b/libraries/wutdevoptab/CMakeLists.txt new file mode 100644 index 0000000..cdd1f3b --- /dev/null +++ b/libraries/wutdevoptab/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.2) +project(wutdevoptab C) + +add_library(wutdevoptab + devoptab_fs.c + devoptab_fs_chdir.c + devoptab_fs_chmod.c + devoptab_fs_close.c + devoptab_fs_dirclose.c + devoptab_fs_dirnext.c + devoptab_fs_diropen.c + devoptab_fs_dirreset.c + devoptab_fs_fchmod.c + devoptab_fs_fstat.c + devoptab_fs_fsync.c + devoptab_fs_getmtime.c + devoptab_fs_link.c + devoptab_fs_mkdir.c + devoptab_fs_open.c + devoptab_fs_read.c + devoptab_fs_rename.c + devoptab_fs_rmdir.c + devoptab_fs_seek.c + devoptab_fs_stat.c + devoptab_fs_statvfs.c + devoptab_fs_truncate.c + devoptab_fs_unlink.c + devoptab_fs_utils.c + devoptab_fs_write.c) + +target_include_directories(wutdevoptab PRIVATE "${WUT_ROOT}/include") + +install(TARGETS wutdevoptab + ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib") diff --git a/libraries/wutdevoptab/devoptab_fs.c b/libraries/wutdevoptab/devoptab_fs.c new file mode 100644 index 0000000..3defbc1 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs.c @@ -0,0 +1,109 @@ +#include "devoptab_fs.h" + +static devoptab_t +__wut_fs_devoptab = +{ + .name = "fs", + .structSize = sizeof(__wut_fs_file_t), + .open_r = __wut_fs_open, + .close_r = __wut_fs_close, + .write_r = __wut_fs_write, + .read_r = __wut_fs_read, + .seek_r = __wut_fs_seek, + .fstat_r = __wut_fs_fstat, + .stat_r = __wut_fs_stat, + .link_r = __wut_fs_link, + .unlink_r = __wut_fs_unlink, + .chdir_r = __wut_fs_chdir, + .rename_r = __wut_fs_rename, + .mkdir_r = __wut_fs_mkdir, + .dirStateSize = sizeof(__wut_fs_dir_t), + .diropen_r = __wut_fs_diropen, + .dirreset_r = __wut_fs_dirreset, + .dirnext_r = __wut_fs_dirnext, + .dirclose_r = __wut_fs_dirclose, + .statvfs_r = __wut_fs_statvfs, + .ftruncate_r = __wut_fs_ftruncate, + .fsync_r = __wut_fs_fsync, + .deviceData = NULL, + .chmod_r = __wut_fs_chmod, + .fchmod_r = __wut_fs_fchmod, + .rmdir_r = __wut_fs_rmdir, +}; + +FSClient * +__wut_devoptab_fs_client = NULL; + +static bool +__wut_fs_initialised = false; + +FSStatus +__init_wut_devoptab() +{ + FSStatus rc = 0; + + if (__wut_fs_initialised) { + return rc; + } + + __wut_devoptab_fs_client = memalign(0x20, sizeof(FSClient)); + FSCmdBlock fsCmd; + FSMountSource mountSource; + char mountPath[0x80]; + char workDir[0x83]; + + FSInit(); + rc = FSAddClient(__wut_devoptab_fs_client, -1); + + if (rc < 0) { + free(__wut_devoptab_fs_client); + return rc; + } + + FSInitCmdBlock(&fsCmd); + + if (rc >= 0) { + int dev = AddDevice(&__wut_fs_devoptab); + + if(dev != -1) { + setDefaultDevice(dev); + __wut_fs_initialised = true; + + // Mount the SD card + rc = FSGetMountSource(__wut_devoptab_fs_client, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, -1); + + if (rc < 0) { + return rc; + } + + rc = FSMount(__wut_devoptab_fs_client, &fsCmd, &mountSource, mountPath, 0x80, -1); + + if (rc >= 0) { + // chdir to SD root for general use + strcpy(workDir, "fs:"); + strcat(workDir, mountPath); + chdir(workDir); + } + } else { + FSDelClient(__wut_devoptab_fs_client, -1); + free(__wut_devoptab_fs_client); + return dev; + } + } + + return rc; +} + +FSStatus +__fini_wut_devoptab() +{ + FSStatus rc = 0; + + if (!__wut_fs_initialised) { + return rc; + } + + FSDelClient(__wut_devoptab_fs_client, -1); + free(__wut_devoptab_fs_client); + return rc; +} diff --git a/libraries/wutdevoptab/devoptab_fs.h b/libraries/wutdevoptab/devoptab_fs.h new file mode 100644 index 0000000..ca60e34 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs.h @@ -0,0 +1,83 @@ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Open file struct + */ +typedef struct +{ + //! FS handle + FSFileHandle fd; + + //! Flags used in open(2) + int flags; + + //! Current file offset + uint32_t offset; +} __wut_fs_file_t; + + +/** + * Open directory struct + */ +typedef struct +{ + //! Should be set to FS_DIRITER_MAGIC + u32 magic; + + //! FS handle + FSDirectoryHandle fd; + + //! Temporary storage for reading entries + FSDirectoryEntry entry_data; +} __wut_fs_dir_t; + +#define FS_DIRITER_MAGIC 0x77696975 + +extern FSClient * +__wut_devoptab_fs_client; + +int __wut_fs_open(struct _reent *r, void *fileStruct, const char *path, + int flags, int mode); +int __wut_fs_close(struct _reent *r, void *fd); +ssize_t __wut_fs_write(struct _reent *r, void *fd, const char *ptr, + size_t len); +ssize_t __wut_fs_read(struct _reent *r, void *fd, char *ptr, size_t len); +off_t __wut_fs_seek(struct _reent *r, void *fd, off_t pos, int dir); +int __wut_fs_fstat(struct _reent *r, void *fd, struct stat *st); +int __wut_fs_stat(struct _reent *r, const char *file, struct stat *st); +int __wut_fs_link(struct _reent *r, const char *existing, + const char *newLink); +int __wut_fs_unlink(struct _reent *r, const char *name); +int __wut_fs_chdir(struct _reent *r, const char *name); +int __wut_fs_rename(struct _reent *r, const char *oldName, + const char *newName); +int __wut_fs_mkdir(struct _reent *r, const char *path, int mode); +DIR_ITER* __wut_fs_diropen(struct _reent *r, DIR_ITER *dirState, + const char *path); +int __wut_fs_dirreset(struct _reent *r, DIR_ITER *dirState); +int __wut_fs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, + struct stat *filestat); +int __wut_fs_dirclose(struct _reent *r, DIR_ITER *dirState); +int __wut_fs_statvfs(struct _reent *r, const char *path, + struct statvfs *buf); +int __wut_fs_ftruncate(struct _reent *r, void *fd, off_t len); +int __wut_fs_fsync(struct _reent *r, void *fd); +int __wut_fs_chmod(struct _reent *r, const char *path, mode_t mode); +int __wut_fs_fchmod(struct _reent *r, void *fd, mode_t mode); +int __wut_fs_rmdir(struct _reent *r, const char *name); + +// devoptab_fs_utils.c +char * __wut_fs_fixpath(struct _reent *r, const char *path); +int __wut_fs_translate_error(FSStatus error); diff --git a/libraries/wutdevoptab/devoptab_fs_chdir.c b/libraries/wutdevoptab/devoptab_fs_chdir.c new file mode 100644 index 0000000..7458909 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_chdir.c @@ -0,0 +1,33 @@ +#include "devoptab_fs.h" + +int +__wut_fs_chdir(struct _reent *r, + const char *name) +{ + FSStatus rc; + + if (name == NULL) { + return -1; + } + + char *path = __wut_fs_fixpath(r, name); + + if (!path) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSChangeDir(__wut_devoptab_fs_client, &fsCmd, path, -1); + free(path); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_chmod.c b/libraries/wutdevoptab/devoptab_fs_chmod.c new file mode 100644 index 0000000..4b891f7 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_chmod.c @@ -0,0 +1,29 @@ +#include "devoptab_fs.h" + +int +__wut_fs_chmod(struct _reent *r, + const char *path, + mode_t mode) +{ + FSStatus rc; + char *path_fix = __wut_fs_fixpath(r, path); + + if (!path_fix) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSChangeMode(__wut_devoptab_fs_client, &fsCmd, path_fix, (FSMode)mode, -1); + free(path_fix); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_close.c b/libraries/wutdevoptab/devoptab_fs_close.c new file mode 100644 index 0000000..8ad28fa --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_close.c @@ -0,0 +1,22 @@ +#include "devoptab_fs.h" + +int +__wut_fs_close(struct _reent *r, + void *fd) +{ + FSStatus rc; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSCloseFile(__wut_devoptab_fs_client, &fsCmd, file->fd, -1); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_dirclose.c b/libraries/wutdevoptab/devoptab_fs_dirclose.c new file mode 100644 index 0000000..adde150 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_dirclose.c @@ -0,0 +1,22 @@ +#include "devoptab_fs.h" + +int +__wut_fs_dirclose(struct _reent *r, + DIR_ITER *dirState) +{ + FSStatus rc; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + __wut_fs_dir_t *dir = (__wut_fs_dir_t *)(dirState->dirStruct); + rc = FSCloseDir(__wut_devoptab_fs_client, &fsCmd, dir->fd, -1); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_dirnext.c b/libraries/wutdevoptab/devoptab_fs_dirnext.c new file mode 100644 index 0000000..1b01306 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_dirnext.c @@ -0,0 +1,49 @@ +#include "devoptab_fs.h" + +int +__wut_fs_dirnext(struct _reent *r, + DIR_ITER *dirState, + char *filename, + struct stat *filestat) +{ + FSStatus rc; + __wut_fs_dir_t *dir = (__wut_fs_dir_t *)(dirState->dirStruct); + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // Fetch the next dir + memset(&dir->entry_data, 0, sizeof(dir->entry_data)); + rc = FSReadDir(__wut_devoptab_fs_client, &fsCmd, dir->fd, &dir->entry_data, -1); + + if (rc < 0) { + // There are no more entries; ENOENT signals end-of-directory + r->_errno = ENOENT; + return -1; + } + + if (rc >= 0) { + memset(filestat, 0, sizeof(struct stat)); + + // Fill in the stat info + filestat->st_ino = 0; + + if (dir->entry_data.info.flags & FS_STAT_DIRECTORY) { + filestat->st_mode = S_IFDIR; + } else { + filestat->st_mode = S_IFREG; + } + + filestat->st_uid = dir->entry_data.info.owner; + filestat->st_gid = dir->entry_data.info.group; + filestat->st_size = dir->entry_data.info.size; + + memset(filename, 0, NAME_MAX); + strcpy(filename, dir->entry_data.name); + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_diropen.c b/libraries/wutdevoptab/devoptab_fs_diropen.c new file mode 100644 index 0000000..2af0ea3 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_diropen.c @@ -0,0 +1,40 @@ +#include "devoptab_fs.h" + +DIR_ITER * +__wut_fs_diropen(struct _reent *r, + DIR_ITER *dirState, + const char *path) +{ + FSDirectoryHandle fd; + FSStatus rc; + + if (path == NULL) { + return NULL; + } + + char *path_fixed = __wut_fs_fixpath(r,path); + + if (!path_fixed) { + r->_errno = ENOMEM; + return NULL; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + __wut_fs_dir_t *dir = (__wut_fs_dir_t *)(dirState->dirStruct); + rc = FSOpenDir(__wut_devoptab_fs_client, &fsCmd, path_fixed, &fd, -1); + + if (rc >= 0) { + dir->magic = FS_DIRITER_MAGIC; + dir->fd = fd; + memset(&dir->entry_data, 0, sizeof(dir->entry_data)); + free(path_fixed); + return dirState; + } + + free(path_fixed); + r->_errno = __wut_fs_translate_error(rc); + return NULL; +} diff --git a/libraries/wutdevoptab/devoptab_fs_dirreset.c b/libraries/wutdevoptab/devoptab_fs_dirreset.c new file mode 100644 index 0000000..36d3384 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_dirreset.c @@ -0,0 +1,23 @@ +#include "devoptab_fs.h" + +int +__wut_fs_dirreset(struct _reent *r, + DIR_ITER *dirState) +{ + FSStatus rc; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + __wut_fs_dir_t *dir = (__wut_fs_dir_t *)(dirState->dirStruct); + rc = FSRewindDir(__wut_devoptab_fs_client, &fsCmd, dir->fd, -1); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} + diff --git a/libraries/wutdevoptab/devoptab_fs_fchmod.c b/libraries/wutdevoptab/devoptab_fs_fchmod.c new file mode 100644 index 0000000..ecab35a --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_fchmod.c @@ -0,0 +1,11 @@ +#include "devoptab_fs.h" + +int +__wut_fs_fchmod(struct _reent *r, + void *fd, + mode_t mode) +{ + //TODO: FSChangeMode and FSStatFile? + r->_errno = ENOSYS; + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_fstat.c b/libraries/wutdevoptab/devoptab_fs_fstat.c new file mode 100644 index 0000000..4da5021 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_fstat.c @@ -0,0 +1,30 @@ +#include "devoptab_fs.h" + +int +__wut_fs_fstat(struct _reent *r, + void *fd, + struct stat *st) +{ + FSStatus rc; + FSStat fsstat; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSGetStatFile(__wut_devoptab_fs_client, &fsCmd, file->fd, &fsstat, -1); + + if (rc >= 0) { + memset(st, 0, sizeof(struct stat)); + st->st_size = fsstat.size; + st->st_uid = fsstat.owner; + st->st_gid = fsstat.group; + st->st_nlink = 1; + st->st_mode = fsstat.mode; + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_fsync.c b/libraries/wutdevoptab/devoptab_fs_fsync.c new file mode 100644 index 0000000..76bc8e5 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_fsync.c @@ -0,0 +1,22 @@ +#include "devoptab_fs.h" + +int +__wut_fs_fsync(struct _reent *r, + void *fd) +{ + FSStatus rc; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSFlushFile(__wut_devoptab_fs_client, &fsCmd, file->fd, -1); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_getmtime.c b/libraries/wutdevoptab/devoptab_fs_getmtime.c new file mode 100644 index 0000000..ff3a9cb --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_getmtime.c @@ -0,0 +1,9 @@ +#include "devoptab_fs.h" + +int +__wut_fs_getmtime(const char *name, + u64 *mtime) +{ + // TODO: Last modified time can probably be get via FSGetStatFile + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_link.c b/libraries/wutdevoptab/devoptab_fs_link.c new file mode 100644 index 0000000..da772fe --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_link.c @@ -0,0 +1,10 @@ +#include "devoptab_fs.h" + +int +__wut_fs_link(struct _reent *r, + const char *existing, + const char *newLink) +{ + r->_errno = ENOSYS; + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_mkdir.c b/libraries/wutdevoptab/devoptab_fs_mkdir.c new file mode 100644 index 0000000..9bdc331 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_mkdir.c @@ -0,0 +1,38 @@ +#include "devoptab_fs.h" + +int +__wut_fs_mkdir(struct _reent *r, + const char *path, + int mode) +{ + FSError rc; + + if (path == NULL) { + return -1; + } + + char *path_fix = __wut_fs_fixpath(r, path); + + if (!path_fix) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // TODO: Use mode to set directory attributes. + rc = FSMakeDir(__wut_devoptab_fs_client, &fsCmd, path_fix, -1); + free(path_fix); + + if (rc == FS_ERROR_ALREADY_EXISTS) { + r->_errno = EEXIST; + return -1; + } else if(rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_open.c b/libraries/wutdevoptab/devoptab_fs_open.c new file mode 100644 index 0000000..813e219 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_open.c @@ -0,0 +1,64 @@ +#include "devoptab_fs.h" + +int +__wut_fs_open(struct _reent *r, + void *fileStruct, + const char *path, + int flags, + int mode) +{ + FSFileHandle fd; + FSStatus rc; + + if (path == NULL) { + return -1; + } + + char *path_fixed = __wut_fs_fixpath(r,path); + if (!path_fixed) { + r->_errno = ENOMEM; + return -1; + } + + // Get pointer to our data + __wut_fs_file_t *file = (__wut_fs_file_t *)fileStruct; + const char *fs_mode; + + // Map flags to open modes + if (flags == 0) { + fs_mode = "r"; + } else if (flags == 2) { + fs_mode = "r+"; + } else if (flags == 0x601) { + fs_mode = "w"; + } else if(flags == 0x602) { + fs_mode = "w+"; + } else if(flags == 0x209) { + fs_mode = "a"; + } else if(flags == 0x20A) { + fs_mode = "a+"; + } else { + free(path_fixed); + r->_errno = EINVAL; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // Open the file + rc = FSOpenFile(__wut_devoptab_fs_client, &fsCmd, path_fixed, fs_mode, &fd, -1); + + if (rc >= 0) { + file->fd = fd; + file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC)); + FSGetPosFile(__wut_devoptab_fs_client, &fsCmd, fd, &file->offset, -1); + free(path_fixed); + return 0; + } + + free(path_fixed); + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_read.c b/libraries/wutdevoptab/devoptab_fs_read.c new file mode 100644 index 0000000..3f3f036 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_read.c @@ -0,0 +1,70 @@ +#include "devoptab_fs.h" + +ssize_t +__wut_fs_read(struct _reent *r, + void *fd, + char *ptr, + size_t len) +{ + FSStatus rc; + u32 bytes, bytesRead = 0; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Check that the file was opened with read access + if ((file->flags & O_ACCMODE) == O_WRONLY) { + r->_errno = EBADF; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + FSStat fsstat; + rc = FSGetStatFile(__wut_devoptab_fs_client, &fsCmd, file->fd, &fsstat, -1); + + if(rc < 0) { + r->_errno = __wut_fs_translate_error(rc); + return -1; + } + + // Copy to internal buffer and read in chunks. + u8 *tmp_buffer = memalign(0x40, 8192); + + while(len > 0) { + size_t toRead = len; + + if (toRead > 8192) { + toRead = 8192; + } + + // Write the data + rc = FSReadFile(__wut_devoptab_fs_client, &fsCmd, tmp_buffer, 1, toRead, file->fd, 0, -1); + + if(rc <= 0) + { + free(tmp_buffer); + + // Return partial transfer + if (bytesRead > 0) { + return bytesRead; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; + } else { + bytes = rc; + } + + // Copy to internal buffer + memcpy(ptr, tmp_buffer, bytes); + + file->offset += bytes; + bytesRead += bytes; + ptr += bytes; + len -= bytes; + } + + free(tmp_buffer); + return bytesRead; +} diff --git a/libraries/wutdevoptab/devoptab_fs_rename.c b/libraries/wutdevoptab/devoptab_fs_rename.c new file mode 100644 index 0000000..3d13715 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_rename.c @@ -0,0 +1,46 @@ +#include "devoptab_fs.h" + +int +__wut_fs_rename(struct _reent *r, + const char *oldName, + const char *newName) +{ + FSStatus rc; + + if (oldName == NULL) { + return -1; + } + + if (newName == NULL) { + return -1; + } + + char *path_old = __wut_fs_fixpath(r, oldName); + + if (!path_old) { + r->_errno = ENOMEM; + return -1; + } + + char *path_new = __wut_fs_fixpath(r, newName); + + if (!path_new) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSRename(__wut_devoptab_fs_client, &fsCmd, path_old, path_new, -1); + free(path_old); + free(path_new); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_rmdir.c b/libraries/wutdevoptab/devoptab_fs_rmdir.c new file mode 100644 index 0000000..32e98c1 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_rmdir.c @@ -0,0 +1,33 @@ +#include "devoptab_fs.h" + +int +__wut_fs_rmdir(struct _reent *r, + const char *name) +{ + FSStatus rc; + + if (name == NULL) { + return -1; + } + + char *path_fix = __wut_fs_fixpath(r, name); + + if (!path_fix) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSRemove(__wut_devoptab_fs_client, &fsCmd, path_fix, -1); + free(path_fix); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_seek.c b/libraries/wutdevoptab/devoptab_fs_seek.c new file mode 100644 index 0000000..ad5ef24 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_seek.c @@ -0,0 +1,64 @@ +#include "devoptab_fs.h" + +off_t +__wut_fs_seek(struct _reent *r, + void *fd, + off_t pos, + int whence) +{ + FSStatus rc; + u64 offset; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + FSStat fsstat; + rc = FSGetStatFile(__wut_devoptab_fs_client, &fsCmd, file->fd, &fsstat, -1); + + if (rc < 0) { + r->_errno = __wut_fs_translate_error(rc); + return -1; + } + + // Find the offset to see from + switch(whence) { + // Set absolute position; start offset is 0 + case SEEK_SET: + offset = 0; + break; + + // Set position relative to the current position + case SEEK_CUR: + offset = file->offset; + break; + + // Set position relative to the end of the file + case SEEK_END: + offset = fsstat.size; + break; + + // An invalid option was provided + default: + r->_errno = EINVAL; + return -1; + } + + // TODO: A better check that prevents overflow. + if(pos < 0 && offset < -pos) { + // Don't allow seek to before the beginning of the file + r->_errno = EINVAL; + return -1; + } + + // Update the current offset + file->offset = offset + pos; + FSStatus result = FSSetPosFile(__wut_devoptab_fs_client, &fsCmd, file->fd, file->offset, -1); + + if (result < 0) { + return result; + } + + return file->offset; +} diff --git a/libraries/wutdevoptab/devoptab_fs_stat.c b/libraries/wutdevoptab/devoptab_fs_stat.c new file mode 100644 index 0000000..472eeaf --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_stat.c @@ -0,0 +1,42 @@ +#include "devoptab_fs.h" + +int +__wut_fs_stat(struct _reent *r, + const char *file, + struct stat *st) +{ + int fd; + FSStatus rc; + + if (file == NULL) { + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // First try open as file + rc = FSOpenFile(__wut_devoptab_fs_client, &fsCmd, file, "r", (FSFileHandle*)&fd, -1); + + if (rc >= 0) { + __wut_fs_file_t tmpfd = { .fd = fd }; + rc = __wut_fs_fstat(r, &tmpfd, st); + FSCloseFile(__wut_devoptab_fs_client, &fsCmd, fd, -1); + return rc; + } + + // File failed, so lets try open as directory + rc = FSOpenDir(__wut_devoptab_fs_client, &fsCmd, file, (FSDirectoryHandle*)&fd, -1); + + if (rc >= 0) { + memset(st, 0, sizeof(struct stat)); + st->st_nlink = 1; + st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + FSCloseDir(__wut_devoptab_fs_client, &fsCmd, fd, -1); + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_statvfs.c b/libraries/wutdevoptab/devoptab_fs_statvfs.c new file mode 100644 index 0000000..7c6669f --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_statvfs.c @@ -0,0 +1,22 @@ +#include "devoptab_fs.h" + +int +__wut_fs_statvfs(struct _reent *r, + const char *path, + struct statvfs *buf) +{ + FSStatus rc; + bool writable = false; + char *path_fix = __wut_fs_fixpath(r, path); + + if (!path_fix) { + r->_errno = ENOMEM; + return -1; + } + + //TODO: FSGetFileSystemInfo + + free(path_fix); + r->_errno = ENOSYS; + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_truncate.c b/libraries/wutdevoptab/devoptab_fs_truncate.c new file mode 100644 index 0000000..5c546c9 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_truncate.c @@ -0,0 +1,36 @@ +#include "devoptab_fs.h" + +int +__wut_fs_ftruncate(struct _reent *r, + void *fd, + off_t len) +{ + FSStatus rc; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Make sure length is non-negative + if (len < 0) { + r->_errno = EINVAL; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // Set the new file size + rc = FSSetPosFile(__wut_devoptab_fs_client, &fsCmd, file->fd, len, -1); + + if (rc >= 0) { + return 0; + } + + rc = FSTruncateFile(__wut_devoptab_fs_client, &fsCmd, file->fd, -1); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_unlink.c b/libraries/wutdevoptab/devoptab_fs_unlink.c new file mode 100644 index 0000000..ce5a40b --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_unlink.c @@ -0,0 +1,33 @@ +#include "devoptab_fs.h" + +int +__wut_fs_unlink(struct _reent *r, + const char *name) +{ + FSStatus rc; + + if (name == NULL) { + return -1; + } + + char *path_fix = __wut_fs_fixpath(r, name); + + if (!path_fix) { + r->_errno = ENOMEM; + return -1; + } + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + rc = FSRemove(__wut_devoptab_fs_client, &fsCmd, path_fix, -1); + free(path_fix); + + if (rc >= 0) { + return 0; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; +} diff --git a/libraries/wutdevoptab/devoptab_fs_utils.c b/libraries/wutdevoptab/devoptab_fs_utils.c new file mode 100644 index 0000000..9b3161f --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_utils.c @@ -0,0 +1,47 @@ +#include "devoptab_fs.h" + +char * +__wut_fs_fixpath(struct _reent *r, + const char *path) +{ + char *p = strchr(path, ':')+1; + + if(!strchr(path, ':')) { + p = (char*)path; + } + + if (strlen(p) > PATH_MAX) { + r->_errno = ENAMETOOLONG; + return NULL; + } + + char *__fixedpath = memalign(0x40, PATH_MAX+1); + + if (__fixedpath == NULL) { + return NULL; + } + + // cwd is handled by coreinit, so just strip the 'fs:' if it exists + strcpy(__fixedpath, p); + return __fixedpath; +} + +int +__wut_fs_translate_error(FSStatus error) +{ + switch (error) { + case FS_STATUS_CANCELLED: + return EINVAL; + case FS_STATUS_EXISTS: + return EEXIST; + case FS_STATUS_NOT_FOUND: + return ENOENT; + case FS_STATUS_STORAGE_FULL: + return ENOSPC; + case FS_ERROR_INVALID_PATH: + return ENAMETOOLONG; + default: + return (int)error; + } +} + diff --git a/libraries/wutdevoptab/devoptab_fs_write.c b/libraries/wutdevoptab/devoptab_fs_write.c new file mode 100644 index 0000000..a57fba3 --- /dev/null +++ b/libraries/wutdevoptab/devoptab_fs_write.c @@ -0,0 +1,61 @@ +#include "devoptab_fs.h" + +ssize_t +__wut_fs_write(struct _reent *r, + void *fd, + const char *ptr, + size_t len) +{ + FSStatus rc; + u32 bytes, bytesWritten = 0; + __wut_fs_file_t *file = (__wut_fs_file_t *)fd; + + // Check that the file was opened with write access + if ((file->flags & O_ACCMODE) == O_RDONLY) { + r->_errno = EBADF; + return -1; + } + + // Copy to internal buffer and write in chunks. + u8 *tmp_buffer = memalign(0x40, 8192); + + while(len > 0) { + size_t toWrite = len; + + if (toWrite > 8192) { + toWrite = 8192; + } + + // Copy to internal buffer + memcpy(tmp_buffer, ptr, toWrite); + + // Set up command block + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + // Write the data + rc = FSWriteFile(__wut_devoptab_fs_client, &fsCmd, tmp_buffer, 1, toWrite, file->fd, 0, -1); + + if (rc < 0) { + free(tmp_buffer); + + // Return partial transfer + if (bytesWritten > 0) { + return bytesWritten; + } + + r->_errno = __wut_fs_translate_error(rc); + return -1; + } else { + bytes = rc; + } + + file->offset += bytes; + bytesWritten += bytes; + ptr += bytes; + len -= bytes; + } + + free(tmp_buffer); + return bytesWritten; +}