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
This commit is contained in:
Crementif
2022-07-23 12:03:07 +02:00
committed by GitHub
parent b2710f8448
commit d621766b2f
11 changed files with 105 additions and 84 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -1,13 +1,6 @@
#include <sys/stat.h>
#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,

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {