From a8acac46e45d516767e503041b1ad16a7a13a6dd Mon Sep 17 00:00:00 2001 From: MCMrARM Date: Wed, 16 May 2018 19:08:20 +0200 Subject: [PATCH] Implement a fs-based dumper (for now for sysupdate) --- source/dumper.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ source/dumper.h | 4 +- source/main.c | 21 ++++++++- 3 files changed, 142 insertions(+), 3 deletions(-) diff --git a/source/dumper.c b/source/dumper.c index a7a8459..8cc23d2 100644 --- a/source/dumper.c +++ b/source/dumper.c @@ -2,6 +2,9 @@ #include "fsext.h" #include #include +#include +#include +#include #include "ccolor.h" #include "util.h" @@ -27,6 +30,7 @@ bool openPartitionFs(FsFileSystem* ret, FsDeviceOperator* fsOperator, u32 partit return false; } printf("Opened card\n"); + return true; } bool dumpPartitionRaw(FsDeviceOperator* fsOperator, u32 partition) { @@ -105,4 +109,120 @@ bool dumpPartitionRaw(FsDeviceOperator* fsOperator, u32 partition) { fsStorageClose(&gameCardStorage); return success; +} + +bool copyFile(const char* source, const char* dest) { + printf("Copying %s...", source); + FILE* inFile = fopen(source, "rb"); + if (!inFile) { + printf("\nFailed to open input file\n"); + return false; + } + FILE* outFile = fopen(dest, "wb"); + if (!outFile) { + printf("\nFailed to open output file\n"); + fclose(outFile); + return false; + } + + fseek(inFile, 0L, SEEK_END); + long int size = ftell(inFile); + fseek(inFile, 0L, SEEK_SET); + + const size_t bufs = 1024 * 1024; + char* buf = (char*) malloc(bufs); + bool success = true; + ssize_t n; + size_t total = 0; + size_t last_report = 0; + printf(" [00%%]"); + syncDisplay(); + while (true) { + n = fread(buf, 1, bufs, inFile); + if (n <= 0) { + if (feof(inFile)) + break; + printf("\nRead error; retrying\n"); + continue; + } + if (fwrite(buf, 1, n, outFile) != n) { + printf("\nWrite error\n"); + success = false; + break; + } + total += n; + if ((total - last_report) > 1024 * 1024) { + hidScanInput(); + u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO); + if (kDown & KEY_B) { + printf("\nCancelled\n"); + success = false; + break; + } + + int p = (int) (total * 100 / size); + if (p >= 100) + p = 99; + printf("\b\b\b\b%02i%%]", p); + syncDisplay(); + last_report = total; + } + } + printf("\b\b\b\b\bDone!\n"); + free(buf); + fclose(inFile); + fclose(outFile); + return success; +} + +#define NAME_BUF_LEN 4096 + +bool _copyDirectory(char* sbuf, size_t source_len, char* dbuf, size_t dest_len) { + DIR* dir = opendir(sbuf); + struct dirent* ent; + sbuf[source_len] = '/'; + dbuf[dest_len] = '/'; + while ((ent = readdir(dir)) != NULL) { + size_t d_name_len = strlen(ent->d_name); + if (source_len + 1 + d_name_len + 1 >= NAME_BUF_LEN || + dest_len + 1 + d_name_len + 1 >= NAME_BUF_LEN) { + printf("Too long file name!\n"); + closedir(dir); + return false; + } + strcpy(sbuf + source_len + 1, ent->d_name); + strcpy(dbuf + dest_len + 1, ent->d_name); + if (ent->d_type == DT_DIR) { + mkdir(dbuf, 0744); + if (!_copyDirectory(sbuf, source_len + 1 + d_name_len, dbuf, dest_len + 1 + d_name_len)) { + closedir(dir); + return false; + } + } else { + if (!copyFile(sbuf, dbuf)) { + closedir(dir); + return false; + } + } + } + return true; +} + +bool copyDirectory(const char* source, const char* dest) { + char sbuf[NAME_BUF_LEN]; + char dbuf[NAME_BUF_LEN]; + size_t source_len = strlen(source); + size_t dest_len = strlen(dest); + if (source_len + 1 >= NAME_BUF_LEN) { + printf("Directory name too long %li: %s\n", source_len + 1, source); + return false; + } + if (dest_len + 1 >= NAME_BUF_LEN) { + printf("Directory name too long %li: %s\n", dest_len + + 1, dest); + return false; + } + strcpy(sbuf, source); + strcpy(dbuf, dest); + mkdir(dbuf, 0744); + return _copyDirectory(sbuf, source_len, dbuf, dest_len); } \ No newline at end of file diff --git a/source/dumper.h b/source/dumper.h index 5a430b3..424d725 100644 --- a/source/dumper.h +++ b/source/dumper.h @@ -4,4 +4,6 @@ void workaroundPartitionZeroAccess(FsDeviceOperator* fsOperator); bool openPartitionFs(FsFileSystem* ret, FsDeviceOperator* fsOperator, u32 partition); -bool dumpPartitionRaw(FsDeviceOperator* fsOperator, u32 partition); \ No newline at end of file +bool dumpPartitionRaw(FsDeviceOperator* fsOperator, u32 partition); +bool copyFile(const char* source, const char* dest); +bool copyDirectory(const char* source, const char* dest); \ No newline at end of file diff --git a/source/main.c b/source/main.c index e60c233..a7a90bb 100644 --- a/source/main.c +++ b/source/main.c @@ -29,13 +29,28 @@ void startOperation(const char* title) { printf(C_DIM "%s\n\n" C_RESET, title); } -void dumpPartitionZero(MenuItem* item) { +void dumpPartitionZeroRaw(MenuItem* item) { startOperation("Raw Dump Partition 0 (SysUpdate)"); workaroundPartitionZeroAccess(&fsOperatorInstance); dumpPartitionRaw(&fsOperatorInstance, 0); menuWaitForAnyButton(); } +void dumpPartitionZeroData(MenuItem* item) { + startOperation("Dump Partition 0 (SysUpdate)"); + workaroundPartitionZeroAccess(&fsOperatorInstance); + FsFileSystem fs; + if (openPartitionFs(&fs, &fsOperatorInstance, 0) && + fsdevMountDevice("gamecard", fs) != -1) { + printf("Copying to /dump_0\n"); + if (copyDirectory("gamecard:/", "/dump_0")) { + printf("Done!\n"); + } + } + fsdevUnmountDevice("dump"); + menuWaitForAnyButton(); +} + void viewPartitionZero() { startOperation("Mount Partition 0 (SysUpdate)"); workaroundPartitionZeroAccess(&fsOperatorInstance); @@ -44,6 +59,7 @@ void viewPartitionZero() { menuWaitForAnyButton(); return; } + fsdevUnmountDevice("test"); // unmount it if it exists if (fsdevMountDevice("test", fs) == -1) { printf("fsdevMountDevice failed\n"); menuWaitForAnyButton(); @@ -54,7 +70,8 @@ void viewPartitionZero() { MenuItem mainMenu[] = { - { .text = "Raw Dump Partition 0 (SysUpdate)", .callback = dumpPartitionZero }, + { .text = "Dump Partition 0 (SysUpdate)", .callback = dumpPartitionZeroData }, + { .text = "Raw Dump Partition 0 (SysUpdate)", .callback = dumpPartitionZeroRaw }, { .text = "View files on Game Card (SysUpdate)", .callback = viewPartitionZero }, { .text = NULL } };