summaryrefslogtreecommitdiff
path: root/src/sys/wii/isfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys/wii/isfs.c')
-rw-r--r--src/sys/wii/isfs.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/src/sys/wii/isfs.c b/src/sys/wii/isfs.c
new file mode 100644
index 0000000..6168864
--- /dev/null
+++ b/src/sys/wii/isfs.c
@@ -0,0 +1,445 @@
+/*
+
+libisfs -- a NAND filesystem devoptab library for the Wii
+
+Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1.The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software in a
+product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+2.Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+3.This notice may not be removed or altered from any source distribution.
+
+
+[In compliance with the above: I patched this code up somewhat so that it
+builds with all warnings. -- Storlek]
+*/
+#include <errno.h>
+#include <ogc/isfs.h>
+#include <ogc/lwp_watchdog.h>
+#include <ogcsys.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/dir.h>
+#include <sys/iosupport.h>
+
+#include "isfs.h"
+
+#define DEVICE_NAME "isfs"
+
+#define FLAG_DIR 1
+#define DIR_SEPARATOR '/'
+#define SECTOR_SIZE 0x800
+#define BUFFER_SIZE 0x8000
+
+#define UNUSED __attribute__((unused))
+
+typedef struct DIR_ENTRY_STRUCT {
+ char *name;
+ char *abspath;
+ u32 size;
+ u8 flags;
+ u32 fileCount;
+ struct DIR_ENTRY_STRUCT *children;
+} DIR_ENTRY;
+
+typedef struct {
+ DIR_ENTRY *entry;
+ s32 isfs_fd;
+ bool inUse;
+} FILE_STRUCT;
+
+typedef struct {
+ DIR_ENTRY *entry;
+ u32 index;
+ bool inUse;
+} DIR_STATE_STRUCT;
+
+static char read_buffer[BUFFER_SIZE] __attribute__((aligned(32)));
+
+static DIR_ENTRY *root = NULL;
+static DIR_ENTRY *current = NULL;
+static s32 dotab_device = -1;
+
+static bool is_dir(DIR_ENTRY *entry) {
+ return entry->flags & FLAG_DIR;
+}
+
+static bool invalid_drive_specifier(const char *path) {
+ if (strchr(path, ':') == NULL) return false;
+ int namelen = strlen(DEVICE_NAME);
+ if (!strncmp(DEVICE_NAME, path, namelen) && path[namelen] == ':') return false;
+ return true;
+}
+
+static DIR_ENTRY *entry_from_path(const char *path) {
+ if (invalid_drive_specifier(path)) return NULL;
+ if (strchr(path, ':') != NULL) path = strchr(path, ':') + 1;
+ DIR_ENTRY *entry;
+ bool found = false;
+ bool notFound = false;
+ const char *pathPosition = path;
+ const char *pathEnd = strchr(path, '\0');
+ if (pathPosition[0] == DIR_SEPARATOR) {
+ entry = root;
+ while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
+ if (pathPosition >= pathEnd) found = true;
+ } else {
+ entry = current;
+ }
+ if (entry == root && !strcmp(".", pathPosition)) found = true;
+ DIR_ENTRY *dir = entry;
+ while (!found && !notFound) {
+ const char *nextPathPosition = strchr(pathPosition, DIR_SEPARATOR);
+ size_t dirnameLength;
+ if (nextPathPosition != NULL) dirnameLength = nextPathPosition - pathPosition;
+ else dirnameLength = strlen(pathPosition);
+ if (dirnameLength >= ISFS_MAXPATHLEN) return NULL;
+
+ u32 fileIndex = 0;
+ while (fileIndex < dir->fileCount && !found && !notFound) {
+ entry = &dir->children[fileIndex];
+ if (dirnameLength == strnlen(entry->name, ISFS_MAXPATHLEN - 1)
+ && !strncasecmp(pathPosition, entry->name, dirnameLength)) found = true;
+ if (found && !is_dir(entry) && nextPathPosition) found = false;
+ if (!found) fileIndex++;
+ }
+
+ if (fileIndex >= dir->fileCount) {
+ notFound = true;
+ found = false;
+ } else if (!nextPathPosition || nextPathPosition >= pathEnd) {
+ found = true;
+ } else if (is_dir(entry)) {
+ dir = entry;
+ pathPosition = nextPathPosition;
+ while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
+ if (pathPosition >= pathEnd) found = true;
+ else found = false;
+ }
+ }
+
+ if (found && !notFound) return entry;
+ return NULL;
+}
+
+static int _ISFS_open_r(struct _reent *r, void *fileStruct, const char *path,
+ UNUSED int flags, UNUSED int mode) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fileStruct;
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ } else if (is_dir(entry)) {
+ r->_errno = EISDIR;
+ return -1;
+ }
+
+ file->entry = entry;
+ file->inUse = true;
+ file->isfs_fd = ISFS_Open(entry->abspath, ISFS_OPEN_READ);
+ if (file->isfs_fd < 0) {
+ if (file->isfs_fd == ISFS_EINVAL) r->_errno = EACCES;
+ else r->_errno = -file->isfs_fd;
+ return -1;
+ }
+
+ return (int)file;
+}
+
+static int _ISFS_close_r(struct _reent *r, int fd) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ file->inUse = false;
+
+ s32 ret = ISFS_Close(file->isfs_fd);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _ISFS_read_r(struct _reent *r, int fd, char *ptr, size_t len) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ if (len <= 0) {
+ return 0;
+ }
+
+ s32 ret = ISFS_Read(file->isfs_fd, read_buffer, len);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ } else if ((size_t) ret < len) {
+ r->_errno = EOVERFLOW;
+ }
+
+ memcpy(ptr, read_buffer, ret);
+ return ret;
+}
+
+static off_t _ISFS_seek_r(struct _reent *r, int fd, off_t pos, int dir) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ s32 ret = ISFS_Seek(file->isfs_fd, pos, dir);
+ if (ret < 0) {
+ r->_errno = -ret;
+ return -1;
+ }
+ return ret;
+}
+
+static void stat_entry(DIR_ENTRY *entry, struct stat *st) {
+ st->st_dev = 0x4957;
+ st->st_ino = 0;
+ st->st_mode = ((is_dir(entry)) ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_nlink = 1;
+ st->st_uid = 1;
+ st->st_gid = 2;
+ st->st_rdev = st->st_dev;
+ st->st_size = entry->size;
+ st->st_atime = 0;
+ st->st_spare1 = 0;
+ st->st_mtime = 0;
+ st->st_spare2 = 0;
+ st->st_ctime = 0;
+ st->st_spare3 = 0;
+ st->st_blksize = SECTOR_SIZE;
+ st->st_blocks = (entry->size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ st->st_spare4[0] = 0;
+ st->st_spare4[1] = 0;
+}
+
+static int _ISFS_fstat_r(struct _reent *r, int fd, struct stat *st) {
+ FILE_STRUCT *file = (FILE_STRUCT *)fd;
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ stat_entry(file->entry, st);
+ return 0;
+}
+
+static int _ISFS_stat_r(struct _reent *r, const char *path, struct stat *st) {
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+ stat_entry(entry, st);
+ return 0;
+}
+
+static int _ISFS_chdir_r(struct _reent *r, const char *path) {
+ DIR_ENTRY *entry = entry_from_path(path);
+ if (!entry) {
+ r->_errno = ENOENT;
+ return -1;
+ } else if (!is_dir(entry)) {
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ return 0;
+}
+
+static DIR_ITER *_ISFS_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ state->entry = entry_from_path(path);
+ if (!state->entry) {
+ r->_errno = ENOENT;
+ return NULL;
+ } else if (!is_dir(state->entry)) {
+ r->_errno = ENOTDIR;
+ return NULL;
+ }
+ state->index = 0;
+ state->inUse = true;
+ return dirState;
+}
+
+static int _ISFS_dirreset_r(struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ state->index = 0;
+ return 0;
+}
+
+static int _ISFS_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ if (state->index >= state->entry->fileCount) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+ DIR_ENTRY *entry = &state->entry->children[state->index++];
+ strncpy(filename, entry->name, ISFS_MAXPATHLEN - 1);
+ stat_entry(entry, st);
+ return 0;
+}
+
+static int _ISFS_dirclose_r(struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ state->inUse = false;
+ return 0;
+}
+
+static const devoptab_t dotab_isfs = {
+ DEVICE_NAME,
+ sizeof(FILE_STRUCT),
+ _ISFS_open_r,
+ _ISFS_close_r,
+ NULL,
+ _ISFS_read_r,
+ _ISFS_seek_r,
+ _ISFS_fstat_r,
+ _ISFS_stat_r,
+ NULL,
+ NULL,
+ _ISFS_chdir_r,
+ NULL,
+ NULL,
+ sizeof(DIR_STATE_STRUCT),
+ _ISFS_diropen_r,
+ _ISFS_dirreset_r,
+ _ISFS_dirnext_r,
+ _ISFS_dirclose_r,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static DIR_ENTRY *add_child_entry(DIR_ENTRY *dir, const char *name) {
+ DIR_ENTRY *newChildren = realloc(dir->children, (dir->fileCount + 1) * sizeof(DIR_ENTRY));
+ if (!newChildren) return NULL;
+ bzero(newChildren + dir->fileCount, sizeof(DIR_ENTRY));
+ dir->children = newChildren;
+ DIR_ENTRY *child = &dir->children[dir->fileCount++];
+ child->name = strdup(name);
+ if (!child->name) return NULL;
+ child->abspath = malloc(strlen(dir->abspath) + (dir != root) + strlen(name) + 1);
+ if (!child->abspath) return NULL;
+ sprintf(child->abspath, "%s/%s", dir == root ? "" : dir->abspath, name);
+ return child;
+}
+
+static bool read_recursive(DIR_ENTRY *parent) {
+ u32 fileCount;
+ s32 ret = ISFS_ReadDir(parent->abspath, NULL, &fileCount);
+ if (ret != ISFS_OK) {
+ s32 fd = ISFS_Open(parent->abspath, ISFS_OPEN_READ);
+ if (fd >= 0) {
+ static fstats st __attribute__((aligned(32)));
+ if (ISFS_GetFileStats(fd, &st) == ISFS_OK) parent->size = st.file_length;
+ ISFS_Close(fd);
+ }
+ return true;
+ }
+ parent->flags = FLAG_DIR;
+ if (fileCount > 0) {
+ if ((ISFS_MAXPATHLEN * fileCount) > BUFFER_SIZE) return false;
+ ret = ISFS_ReadDir(parent->abspath, read_buffer, &fileCount);
+ if (ret != ISFS_OK) return false;
+ u32 fileNum;
+ char *name = read_buffer;
+ for (fileNum = 0; fileNum < fileCount; fileNum++) {
+ DIR_ENTRY *child = add_child_entry(parent, name);
+ if (!child) return false;
+ name += strlen(name) + 1;
+ }
+ for (fileNum = 0; fileNum < fileCount; fileNum++)
+ if (!read_recursive(parent->children + fileNum))
+ return false;
+ }
+ return true;
+}
+
+static bool read_isfs() {
+ root = malloc(sizeof(DIR_ENTRY));
+ if (!root) return false;
+ bzero(root, sizeof(DIR_ENTRY));
+ current = root;
+ root->name = strdup("/");
+ if (!root->name) return false;
+ root->abspath = strdup("/");
+ if (!root->abspath) return false;
+ return read_recursive(root);
+}
+
+static void cleanup_recursive(DIR_ENTRY *entry) {
+ u32 i;
+ for (i = 0; i < entry->fileCount; i++) cleanup_recursive(&entry->children[i]);
+ if (entry->children) free(entry->children);
+ if (entry->name) free(entry->name);
+ if (entry->abspath) free(entry->abspath);
+}
+
+bool ISFS_Mount() {
+ ISFS_Unmount();
+ bool success = read_isfs() && (dotab_device = AddDevice(&dotab_isfs)) >= 0;
+ if (!success) ISFS_Unmount();
+ return success;
+}
+
+bool ISFS_Unmount() {
+ if (root) {
+ cleanup_recursive(root);
+ free(root);
+ root = NULL;
+ }
+ current = root;
+ if (dotab_device >= 0) {
+ dotab_device = -1;
+ return !RemoveDevice(DEVICE_NAME ":");
+ }
+ return true;
+}
+
+#include "certs_bin.h"
+#include "su_tik_bin.h"
+#include "su_tmd_bin.h"
+
+s32 ISFS_SU() {
+ u32 key = 0;
+ return ES_Identify((signed_blob *) certs_bin, sizeof(certs_bin),
+ (signed_blob *) su_tmd_bin, sizeof(su_tmd_bin),
+ (signed_blob *) su_tik_bin, sizeof(su_tik_bin),
+ &key);
+}
© All Rights Reserved