From d621766b2f1dcb469037c53015c6159b0b25fc3f Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Sat, 23 Jul 2022 12:03:07 +0200 Subject: [PATCH] devoptab: Implement various functions and fixes (#223) - Improved error codes for unlink and rmdir regarding non-empty directories. - Return ENOENT on empty strings since it softlocks the Wii U. Since the devoptab relies on the cwd it's pretty easy to run into with stuff like recursive path creation for example. - Clean up open_r mode_t->string conversion - Improved FSStat->mode_t conversion - Add FSOpenFileEx and flags --- include/coreinit/filesystem.h | 33 +++++++++++++ include/coreinit/filesystem_fsa.h | 15 ------ libraries/wutdevoptab/devoptab_fs.h | 4 +- libraries/wutdevoptab/devoptab_fs_chmod.c | 9 +--- libraries/wutdevoptab/devoptab_fs_dirnext.c | 14 +----- libraries/wutdevoptab/devoptab_fs_fstat.c | 10 +--- libraries/wutdevoptab/devoptab_fs_open.c | 33 +++++++++---- libraries/wutdevoptab/devoptab_fs_rmdir.c | 2 +- libraries/wutdevoptab/devoptab_fs_stat.c | 15 +----- libraries/wutdevoptab/devoptab_fs_unlink.c | 2 +- libraries/wutdevoptab/devoptab_fs_utils.c | 52 ++++++++++++++++----- 11 files changed, 105 insertions(+), 84 deletions(-) diff --git a/include/coreinit/filesystem.h b/include/coreinit/filesystem.h index 72949636..d22ca2a8 100644 --- a/include/coreinit/filesystem.h +++ b/include/coreinit/filesystem.h @@ -193,6 +193,16 @@ typedef enum FSMountSourceType { FS_MOUNT_SOURCE_UNK = 1, } FSMountSourceType; +typedef enum FSOpenFileFlags +{ + //! Open file normally + FS_OPEN_FLAG_NONE = (0 << 0), + //! Open (new) unencrypted file. Used for vWii files since it allows for direct access without the Wii U filesystem through it's block address. + FS_OPEN_FLAG_UNENCRYPTED = (1 << 0), + //! Preallocates new file size using given size + FS_OPEN_FLAG_PREALLOC_SIZE = (1 << 1) +} FSOpenFileFlags; + typedef void(*FSAsyncCallback)(FSClient *, FSCmdBlock *, FSStatus, uint32_t); typedef void(*FSStateChangeCallback)(FSClient *, FSVolumeState, void *); @@ -562,6 +572,29 @@ FSCloseFileAsync(FSClient *client, FSErrorFlag errorMask, FSAsyncData *asyncData); +FSStatus +FSOpenFileEx(FSClient *client, + FSCmdBlock *block, + const char *path, + const char *mode, + FSMode createMode, + FSOpenFileFlags openFlag, + uint32_t preallocSize, + FSFileHandle *handle, + FSErrorFlag errorMask); + +FSStatus +FSOpenFileExAsync(FSClient *client, + FSCmdBlock *block, + const char *path, + const char *mode, + FSFileHandle *outHandle, + FSMode createMode, + FSOpenFileFlags openFlag, + uint32_t preallocSize, + FSErrorFlag errorMask, + FSAsyncData *asyncData); + FSStatus FSOpenDir(FSClient *client, FSCmdBlock *block, diff --git a/include/coreinit/filesystem_fsa.h b/include/coreinit/filesystem_fsa.h index bf3c175c..06485655 100644 --- a/include/coreinit/filesystem_fsa.h +++ b/include/coreinit/filesystem_fsa.h @@ -280,15 +280,6 @@ WUT_CHECK_OFFSET(FSAClientAttachAsyncData, 0x04, userContext); WUT_CHECK_OFFSET(FSAClientAttachAsyncData, 0x08, ioMsgQueue); WUT_CHECK_SIZE(FSAClientAttachAsyncData, 0xC); -typedef enum FSOpenFileFlags { - //! Open file normally - FS_OPEN_FLAG_NONE = (0 << 0), - //! Open (new) encrypted file - FS_OPEN_FLAG_ENCRYPTED = (1 << 0), - //! Preallocates new file size using given size - FS_OPEN_FLAG_PREALLOC_SIZE = (1 << 1) -} FSOpenFileFlags; - /** * Block information. */ @@ -392,12 +383,6 @@ FSAChangeMode(FSAClientHandle client, const char *path, FSMode permission); -FSError -FSOpenFileEx(FSAClientHandle client, - const char *path, - const char *mode, - FSAFileHandle *outFileHandle); - FSError FSAOpenFileEx(FSAClientHandle client, const char *path, diff --git a/libraries/wutdevoptab/devoptab_fs.h b/libraries/wutdevoptab/devoptab_fs.h index 67ed4835..05f2169f 100644 --- a/libraries/wutdevoptab/devoptab_fs.h +++ b/libraries/wutdevoptab/devoptab_fs.h @@ -83,4 +83,6 @@ int __wut_fs_utimes(struct _reent *r, const char *filename, const struct t char * __wut_fs_fixpath(struct _reent *r, const char *path); int __wut_fs_translate_error(FSStatus error); time_t __wut_fs_translate_time(FSTime timeValue); -mode_t __wut_fs_translate_mode(FSStat fileStat); +mode_t __wut_fs_translate_stat_mode(FSStat *fsStat); +FSMode __wut_fs_translate_permission_mode(mode_t mode); +void __wut_fs_translate_stat(FSStat *fsStat, struct stat* posStat); diff --git a/libraries/wutdevoptab/devoptab_fs_chmod.c b/libraries/wutdevoptab/devoptab_fs_chmod.c index bc4de68a..a644d2f6 100644 --- a/libraries/wutdevoptab/devoptab_fs_chmod.c +++ b/libraries/wutdevoptab/devoptab_fs_chmod.c @@ -1,13 +1,6 @@ #include #include "devoptab_fs.h" -static inline FSMode -__wut_fs_convert_mode(mode_t mode) -{ - // Convert normal Unix octal permission bits into CafeOS hexadecimal permission bits - return (FSMode) (((mode & S_IRWXU) << 2) | ((mode & S_IRWXG) << 1) | (mode & S_IRWXO)); -} - int __wut_fs_chmod(struct _reent *r, const char *path, @@ -26,7 +19,7 @@ __wut_fs_chmod(struct _reent *r, return -1; } - FSMode translatedMode = __wut_fs_convert_mode(mode); + FSMode translatedMode = __wut_fs_translate_permission_mode(mode); FSInitCmdBlock(&cmd); status = FSChangeMode(__wut_devoptab_fs_client, &cmd, fixedPath, diff --git a/libraries/wutdevoptab/devoptab_fs_dirnext.c b/libraries/wutdevoptab/devoptab_fs_dirnext.c index 83f57b78..e145516d 100644 --- a/libraries/wutdevoptab/devoptab_fs_dirnext.c +++ b/libraries/wutdevoptab/devoptab_fs_dirnext.c @@ -25,19 +25,7 @@ __wut_fs_dirnext(struct _reent *r, return -1; } - // Fill in the stat info - memset(filestat, 0, sizeof(struct stat)); - 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; + __wut_fs_translate_stat(&dir->entry_data.info, filestat); memset(filename, 0, NAME_MAX); strcpy(filename, dir->entry_data.name); diff --git a/libraries/wutdevoptab/devoptab_fs_fstat.c b/libraries/wutdevoptab/devoptab_fs_fstat.c index 95d1eb8a..78fa880c 100644 --- a/libraries/wutdevoptab/devoptab_fs_fstat.c +++ b/libraries/wutdevoptab/devoptab_fs_fstat.c @@ -24,15 +24,7 @@ __wut_fs_fstat(struct _reent *r, return -1; } - 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 = __wut_fs_translate_mode(fsStat); - st->st_atime = __wut_fs_translate_time(fsStat.modified); - st->st_ctime = __wut_fs_translate_time(fsStat.created); - st->st_mtime = __wut_fs_translate_time(fsStat.modified); + __wut_fs_translate_stat(&fsStat, st); return 0; } diff --git a/libraries/wutdevoptab/devoptab_fs_open.c b/libraries/wutdevoptab/devoptab_fs_open.c index c6628527..4888d70e 100644 --- a/libraries/wutdevoptab/devoptab_fs_open.c +++ b/libraries/wutdevoptab/devoptab_fs_open.c @@ -1,5 +1,8 @@ #include "devoptab_fs.h" +// Extended "magic" value that allows opening files with FS_OPEN_FLAG_UNENCRYPTED in underlying FSOpenFileEx() call similar to O_DIRECTORY +#define O_UNENCRYPTED 0x4000000 + int __wut_fs_open(struct _reent *r, void *fileStruct, @@ -19,32 +22,34 @@ __wut_fs_open(struct _reent *r, } // Map flags to open modes - if (flags == 0) { + int commonFlagMask = O_CREAT | O_TRUNC | O_APPEND; + if (((flags & O_ACCMODE) == O_RDONLY) && !(flags & commonFlagMask)) { fsMode = "r"; - } else if (flags == 2) { + } else if (((flags & O_ACCMODE) == O_RDWR) && !(flags & commonFlagMask)) { fsMode = "r+"; - } else if (flags == 0x601) { + } else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT | O_TRUNC))) { fsMode = "w"; - } else if(flags == 0x602) { + } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_TRUNC))) { fsMode = "w+"; - } else if(flags == 0x209) { + } else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) { fsMode = "a"; - } else if(flags == 0x20A) { + } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) { fsMode = "a+"; } else { r->_errno = EINVAL; return -1; } - char *fixedPath = __wut_fs_fixpath(r,path); + char *fixedPath = __wut_fs_fixpath(r, path); if (!fixedPath) { return -1; } // Open the file FSInitCmdBlock(&cmd); - status = FSOpenFile(__wut_devoptab_fs_client, &cmd, fixedPath, fsMode, &fd, - FS_ERROR_FLAG_ALL); + FSOpenFileFlags openFlags = (mode & O_UNENCRYPTED) ? FS_OPEN_FLAG_UNENCRYPTED : FS_OPEN_FLAG_NONE; + uint32_t preallocSize = 0; + status = FSOpenFileEx(__wut_devoptab_fs_client, &cmd, fixedPath, fsMode, __wut_fs_translate_permission_mode(mode), openFlags, preallocSize, &fd, FS_ERROR_FLAG_ALL); free(fixedPath); if (status < 0) { r->_errno = __wut_fs_translate_error(status); @@ -54,6 +59,14 @@ __wut_fs_open(struct _reent *r, file = (__wut_fs_file_t *)fileStruct; file->fd = fd; file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC)); - FSGetPosFile(__wut_devoptab_fs_client, &cmd, fd, &file->offset, FS_ERROR_FLAG_ALL); + if (flags & O_APPEND) { + status = FSGetPosFile(__wut_devoptab_fs_client, &cmd, fd, &file->offset, FS_ERROR_FLAG_ALL); + if (status < 0) { + FSCloseFile(__wut_devoptab_fs_client, &cmd, fd, FS_ERROR_FLAG_ALL); + r->_errno = __wut_fs_translate_error(status); + return -1; + } + } + return 0; } diff --git a/libraries/wutdevoptab/devoptab_fs_rmdir.c b/libraries/wutdevoptab/devoptab_fs_rmdir.c index 70b89eb6..0c252681 100644 --- a/libraries/wutdevoptab/devoptab_fs_rmdir.c +++ b/libraries/wutdevoptab/devoptab_fs_rmdir.c @@ -21,7 +21,7 @@ __wut_fs_rmdir(struct _reent *r, status = FSRemove(__wut_devoptab_fs_client, &cmd, fixedPath, FS_ERROR_FLAG_ALL); free(fixedPath); if (status < 0) { - r->_errno = __wut_fs_translate_error(status); + r->_errno = status == FS_STATUS_EXISTS ? ENOTEMPTY : __wut_fs_translate_error(status); return -1; } diff --git a/libraries/wutdevoptab/devoptab_fs_stat.c b/libraries/wutdevoptab/devoptab_fs_stat.c index 1ad8c41b..9a189305 100644 --- a/libraries/wutdevoptab/devoptab_fs_stat.c +++ b/libraries/wutdevoptab/devoptab_fs_stat.c @@ -20,7 +20,6 @@ __wut_fs_stat(struct _reent *r, FSInitCmdBlock(&cmd); - // First try open as file status = FSGetStat(__wut_devoptab_fs_client, &cmd, fixedPath, &fsStat, FS_ERROR_FLAG_ALL); if (status < 0) { free(fixedPath); @@ -29,19 +28,7 @@ __wut_fs_stat(struct _reent *r, } free(fixedPath); - memset(st, 0, sizeof(struct stat)); - - st->st_nlink = 1; - st->st_atime = __wut_fs_translate_time(fsStat.modified); - st->st_ctime = __wut_fs_translate_time(fsStat.created); - st->st_mtime = __wut_fs_translate_time(fsStat.modified); - st->st_mode = __wut_fs_translate_mode(fsStat); - - if (!(fsStat.flags & FS_STAT_DIRECTORY)) { - st->st_size = fsStat.size; - st->st_uid = fsStat.owner; - st->st_gid = fsStat.group; - } + __wut_fs_translate_stat(&fsStat, st); return 0; } \ No newline at end of file diff --git a/libraries/wutdevoptab/devoptab_fs_unlink.c b/libraries/wutdevoptab/devoptab_fs_unlink.c index ae0c0da9..d53c6ec4 100644 --- a/libraries/wutdevoptab/devoptab_fs_unlink.c +++ b/libraries/wutdevoptab/devoptab_fs_unlink.c @@ -22,7 +22,7 @@ __wut_fs_unlink(struct _reent *r, status = FSRemove(__wut_devoptab_fs_client, &cmd, fixedPath, FS_ERROR_FLAG_ALL); free(fixedPath); if (status < 0) { - r->_errno = __wut_fs_translate_error(status); + r->_errno = status == FS_STATUS_EXISTS ? EISDIR : __wut_fs_translate_error(status); return -1; } diff --git a/libraries/wutdevoptab/devoptab_fs_utils.c b/libraries/wutdevoptab/devoptab_fs_utils.c index 3ac326f6..bc2b5f91 100644 --- a/libraries/wutdevoptab/devoptab_fs_utils.c +++ b/libraries/wutdevoptab/devoptab_fs_utils.c @@ -17,12 +17,19 @@ __wut_fs_fixpath(struct _reent *r, p = (char *) path; } - if (strlen(p) > PATH_MAX) { + size_t pathLength = strlen(p); + if (pathLength > FS_MAX_PATH) { r->_errno = ENAMETOOLONG; return NULL; } - fixedPath = memalign(0x40, PATH_MAX + 1); + // wii u softlocks on empty strings so give expected error back + if (pathLength == 0) { + r->_errno = ENOENT; + return NULL; + } + + fixedPath = memalign(0x40, FS_MAX_PATH + 1); if (!fixedPath) { r->_errno = ENOMEM; return NULL; @@ -33,35 +40,56 @@ __wut_fs_fixpath(struct _reent *r, return fixedPath; } -mode_t __wut_fs_translate_mode(FSStat fileStat) { +mode_t __wut_fs_translate_stat_mode(FSStat* fsStat) { mode_t retMode = 0; - if ((fileStat.flags & FS_STAT_LINK) == FS_STAT_LINK) { + if ((fsStat->flags & FS_STAT_LINK) == FS_STAT_LINK) { retMode |= S_IFLNK; - } else if ((fileStat.flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) { + } else if ((fsStat->flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) { retMode |= S_IFDIR; - } else if ((fileStat.flags & FS_STAT_FILE) == FS_STAT_FILE) { + } else if ((fsStat->flags & FS_STAT_FILE) == FS_STAT_FILE) { retMode |= S_IFREG; - } else if(fileStat.size == 0) { + } else if (fsStat->size == 0) { // Mounted paths like /vol/external01 have no flags set. // If no flag is set and the size is 0, it's a (root) dir retMode |= S_IFDIR; - } else if (fileStat.size > 0) { + } else if (fsStat->size > 0) { // Some regular Wii U files have no type info but will have a size retMode |= S_IFREG; } - mode_t ownerFlags = (fileStat.mode & (FS_MODE_READ_OWNER | FS_MODE_WRITE_OWNER | FS_MODE_EXEC_OWNER)) >> 2; - mode_t groupFlags = (fileStat.mode & (FS_MODE_READ_GROUP | FS_MODE_WRITE_GROUP | FS_MODE_EXEC_GROUP)) >> 1; - mode_t userFlags = (fileStat.mode & (FS_MODE_READ_OTHER | FS_MODE_WRITE_OTHER | FS_MODE_EXEC_OTHER)); + // Convert normal CafeOS hexadecimal permission bits into Unix octal permission bits + mode_t permissionMode = (((fsStat->mode >> 2) & S_IRWXU) | ((fsStat->mode >> 1) & S_IRWXG) | (fsStat->mode & S_IRWXO)); - return retMode | ownerFlags | groupFlags | userFlags; + return retMode | permissionMode; +} + +FSMode __wut_fs_translate_permission_mode(mode_t mode) { + // Convert normal Unix octal permission bits into CafeOS hexadecimal permission bits + return (FSMode) (((mode & S_IRWXU) << 2) | ((mode & S_IRWXG) << 1) | (mode & S_IRWXO)); } time_t __wut_fs_translate_time(FSTime timeValue) { return (timeValue /1000000) + EPOCH_DIFF_SECS(WIIU_FSTIME_EPOCH_YEAR); } +void __wut_fs_translate_stat(FSStat* fsStat, struct stat* posStat) { + memset(posStat, 0, sizeof(struct stat)); + posStat->st_dev = (dev_t)__wut_devoptab_fs_client; + posStat->st_ino = fsStat->entryId; + posStat->st_mode = __wut_fs_translate_stat_mode(fsStat); + posStat->st_nlink = 1; + posStat->st_uid = fsStat->owner; + posStat->st_gid = fsStat->group; + posStat->st_rdev = posStat->st_dev; + posStat->st_size = fsStat->size; + posStat->st_atime = __wut_fs_translate_time(fsStat->modified); + posStat->st_ctime = __wut_fs_translate_time(fsStat->created); + posStat->st_mtime = __wut_fs_translate_time(fsStat->modified); + posStat->st_blksize = 512; + posStat->st_blocks = (posStat->st_size + posStat->st_blksize - 1) / posStat->st_size; +} + int __wut_fs_translate_error(FSStatus error) { switch ((int) error) {