mirror of
https://github.com/wiiu-env/libmocha.git
synced 2024-11-14 16:05:07 +01:00
144 lines
6.2 KiB
C++
144 lines
6.2 KiB
C++
#include "devoptab_fsa.h"
|
|
#include "logger.h"
|
|
#include <mutex>
|
|
|
|
// Extended "magic" value that allows opening files with FS_OPEN_FLAG_UNENCRYPTED in underlying FSOpenFileEx() call similar to O_DIRECTORY
|
|
#ifndef O_UNENCRYPTED
|
|
#define O_UNENCRYPTED 0x4000000
|
|
#endif
|
|
|
|
int __fsa_open(struct _reent *r,
|
|
void *fileStruct,
|
|
const char *path,
|
|
int flags,
|
|
int mode) {
|
|
FSAFileHandle fd;
|
|
FSError status;
|
|
const char *fsMode;
|
|
|
|
if (!fileStruct || !path) {
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
bool createFileIfNotFound = false;
|
|
bool failIfFileNotFound = false;
|
|
// Map flags to open modes
|
|
int commonFlagMask = O_CREAT | O_TRUNC | O_APPEND;
|
|
if (((flags & O_ACCMODE) == O_RDONLY) && !(flags & commonFlagMask)) {
|
|
fsMode = "r";
|
|
} else if (((flags & O_ACCMODE) == O_RDWR) && !(flags & commonFlagMask)) {
|
|
fsMode = "r+";
|
|
} else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT | O_TRUNC))) {
|
|
fsMode = "w";
|
|
} else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_TRUNC))) {
|
|
fsMode = "w+";
|
|
} else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) {
|
|
fsMode = "a";
|
|
} else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) {
|
|
fsMode = "a+";
|
|
} else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT))) {
|
|
// Cafe OS doesn't have a matching mode for this, so we have to be creative and create the file.
|
|
createFileIfNotFound = true;
|
|
// It's not possible to open a file with write only mode which doesn't truncate the file
|
|
// Technically we could read from the file, but our read implementation is blocking this.
|
|
fsMode = "r+";
|
|
} else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_APPEND))) {
|
|
// Cafe OS doesn't have a matching mode for this, so we have to check if the file exists.
|
|
failIfFileNotFound = true;
|
|
fsMode = "a";
|
|
} else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_TRUNC))) {
|
|
// As above
|
|
failIfFileNotFound = true;
|
|
fsMode = "w";
|
|
} else {
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
char *fixedPath = __fsa_fixpath(r, path);
|
|
if (!fixedPath) {
|
|
r->_errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
auto *file = (__fsa_file_t *) fileStruct;
|
|
strncpy(file->fullPath, fixedPath, sizeof(file->fullPath) - 1);
|
|
free(fixedPath);
|
|
|
|
// Prepare flags
|
|
FSOpenFileFlags openFlags = (flags & O_UNENCRYPTED) ? FS_OPEN_FLAG_UNENCRYPTED : FS_OPEN_FLAG_NONE;
|
|
FSMode translatedMode = __fsa_translate_permission_mode(mode);
|
|
uint32_t preAllocSize = 0;
|
|
|
|
// Init mutex and lock
|
|
file->mutex.init(file->fullPath);
|
|
std::lock_guard<MutexWrapper> lock(file->mutex);
|
|
|
|
auto *deviceData = (FSADeviceData *) r->deviceData;
|
|
|
|
if (createFileIfNotFound || failIfFileNotFound || (flags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
|
|
// Check if file exists
|
|
FSAStat stat;
|
|
status = FSAGetStat(deviceData->clientHandle, file->fullPath, &stat);
|
|
if (status == FS_ERROR_NOT_FOUND) {
|
|
if (createFileIfNotFound) { // Create new file if needed
|
|
status = FSAOpenFileEx(deviceData->clientHandle, file->fullPath, "w", translatedMode,
|
|
openFlags, preAllocSize, &fd);
|
|
if (status == FS_ERROR_OK) {
|
|
if (FSACloseFile(deviceData->clientHandle, fd) != FS_ERROR_OK) {
|
|
DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) (%s) failed: %s",
|
|
deviceData->clientHandle, fd, file->fullPath, FSAGetStatusStr(status));
|
|
}
|
|
fd = -1;
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, 0x%08X) failed: %s",
|
|
deviceData->clientHandle, file->fullPath, "w", translatedMode, openFlags, preAllocSize, &fd,
|
|
FSAGetStatusStr(status));
|
|
r->_errno = __fsa_translate_error(status);
|
|
return -1;
|
|
}
|
|
} else if (failIfFileNotFound) { // Return an error if we don't we create new files
|
|
r->_errno = __fsa_translate_error(status);
|
|
return -1;
|
|
}
|
|
} else if (status == FS_ERROR_OK) {
|
|
// If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
|
|
if ((flags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
|
|
r->_errno = EEXIST;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
status = FSAOpenFileEx(deviceData->clientHandle, file->fullPath, fsMode, translatedMode, openFlags, preAllocSize, &fd);
|
|
if (status < 0) {
|
|
DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, 0x%08X) failed: %s",
|
|
deviceData->clientHandle, file->fullPath, fsMode, translatedMode, openFlags, preAllocSize, &fd,
|
|
FSAGetStatusStr(status));
|
|
r->_errno = __fsa_translate_error(status);
|
|
return -1;
|
|
}
|
|
|
|
file->fd = fd;
|
|
file->flags = (flags & (O_ACCMODE | O_APPEND | O_SYNC));
|
|
// Is always 0, even if O_APPEND is set.
|
|
file->offset = 0;
|
|
|
|
if (flags & O_APPEND) {
|
|
FSAStat stat;
|
|
status = FSAGetStatFile(deviceData->clientHandle, fd, &stat);
|
|
if (status < 0) {
|
|
DEBUG_FUNCTION_LINE_ERR("FSAGetStatFile(0x%08X, 0x%08X, 0x%08X) (%s) failed: %s",
|
|
deviceData->clientHandle, fd, &stat, file->fullPath, FSAGetStatusStr(status));
|
|
r->_errno = __fsa_translate_error(status);
|
|
if (FSACloseFile(deviceData->clientHandle, fd) < 0) {
|
|
DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) (%d) failed: %s",
|
|
deviceData->clientHandle, fd, file->fullPath, FSAGetStatusStr(status));
|
|
}
|
|
return -1;
|
|
}
|
|
file->appendOffset = stat.size;
|
|
}
|
|
return 0;
|
|
} |