From baa14561eb7f513db973e6ff21dc5dc2065587f1 Mon Sep 17 00:00:00 2001 From: Pk11 Date: Wed, 26 May 2021 05:47:51 -0500 Subject: [PATCH] Add safety checks for low SD card space (#90) * Some attempt. * Enable some-safety-freespace-work nightlies * Upload elf with nightly * *Derp fixes.* * Bye Bye testing things. * Revert "Upload elf with nightly" This reverts commit 303968cd78e466a3fcc010915565d8265acaa76a. * Add safety checks to fcopy Co-authored-by: StackZ <47382115+SuperSaiyajinStackZ@users.noreply.github.com> --- include/utils/files.hpp | 1 + include/utils/stringutils.hpp | 2 +- source/init.cpp | 4 - source/menu/entryInfo.cpp | 1 + source/menu/settings.cpp | 6 +- source/utils/cia.cpp | 46 ++++++---- source/utils/download.cpp | 157 ++++++++++++++++++---------------- source/utils/extract.cpp | 3 + source/utils/fileBrowse.cpp | 16 +++- source/utils/files.cpp | 11 +++ source/utils/stringutils.cpp | 13 +-- 11 files changed, 150 insertions(+), 110 deletions(-) diff --git a/include/utils/files.hpp b/include/utils/files.hpp index 90a8015..b3afc75 100644 --- a/include/utils/files.hpp +++ b/include/utils/files.hpp @@ -34,5 +34,6 @@ Result openFile(Handle *fileHandle, const char *path, bool write); Result deleteFile(const char *path); Result removeDir(const char *path); Result removeDirRecursive(const char *path); +u64 getAvailableSpace(); #endif \ No newline at end of file diff --git a/include/utils/stringutils.hpp b/include/utils/stringutils.hpp index 953cce6..84f47d5 100644 --- a/include/utils/stringutils.hpp +++ b/include/utils/stringutils.hpp @@ -34,7 +34,7 @@ namespace StringUtils { std::string lower_case(const std::string &str); std::string FetchStringsFromVector(const std::vector &fetch); - std::string formatBytes(int bytes); + std::string formatBytes(u64 bytes); std::string GetMarkString(int marks); std::vector GetMarks(int marks); std::string format(const char *fmt_str, ...); diff --git a/source/init.cpp b/source/init.cpp index cc15e82..c9b70fa 100644 --- a/source/init.cpp +++ b/source/init.cpp @@ -196,10 +196,6 @@ Result Init::MainLoop() { if (!exiting) Gui::ScreenLogic(hDown, hHeld, touch, true, false); - if (aptCheckHomePressRejected()) { - if (Msg::promptMsg(Lang::get("FEATURE_SIDE_EFFECTS"))) aptSetHomeAllowed(true); - }; - if (exiting) { if (hDown & KEY_START) fullExit = true; // Make it optionally faster. diff --git a/source/menu/entryInfo.cpp b/source/menu/entryInfo.cpp index cd1c13f..17fe781 100644 --- a/source/menu/entryInfo.cpp +++ b/source/menu/entryInfo.cpp @@ -25,6 +25,7 @@ */ #include "common.hpp" +#include "files.hpp" #include "storeUtils.hpp" #include "structs.hpp" diff --git a/source/menu/settings.cpp b/source/menu/settings.cpp index 2568dfb..a474fea 100644 --- a/source/menu/settings.cpp +++ b/source/menu/settings.cpp @@ -272,8 +272,7 @@ static void SettingsHandleMain(int &page, bool &dspSettings, int &storeMode, int Overlays::ShowCredits(); } else if (touching(touch, mainButtons[6])) { - if (QueueRuns) exiting = Msg::promptMsg(Lang::get("FEATURE_SIDE_EFFECTS")); - else exiting = true; + if (!QueueRuns) exiting = true; } } @@ -313,8 +312,7 @@ static void SettingsHandleMain(int &page, bool &dspSettings, int &storeMode, int break; case 6: - if (QueueRuns) exiting = Msg::promptMsg(Lang::get("FEATURE_SIDE_EFFECTS")); - else exiting = true; + if (!QueueRuns) exiting = true; break; } } diff --git a/source/utils/cia.cpp b/source/utils/cia.cpp index 65e977d..72c08a7 100644 --- a/source/utils/cia.cpp +++ b/source/utils/cia.cpp @@ -123,32 +123,40 @@ Result Title::Install(const char *ciaPath, bool updatingSelf) { ret = FSFILE_GetSize(fileHandle, &size); if (R_FAILED(ret)) { printf("Error in:\nFSFILE_GetSize\n"); + FSFILE_Close(fileHandle); return ret; } - ret = AM_StartCiaInstall(media, &ciaHandle); - if (R_FAILED(ret)) { - printf("Error in:\nAM_StartCiaInstall\n"); - return ret; - } + if (getAvailableSpace() >= size) { + ret = AM_StartCiaInstall(media, &ciaHandle); + if (R_FAILED(ret)) { + printf("Error in:\nAM_StartCiaInstall\n"); + FSFILE_Close(fileHandle); + return ret; + } - u32 toRead = 0x200000; - u8 *buf = new u8[toRead]; + u32 toRead = 0x200000; + u8 *buf = new u8[toRead]; - if (!buf) return -1; + if (!buf) { + FSFILE_Close(fileHandle); + return -1; + } - installSize = size; - do { - FSFILE_Read(fileHandle, &bytes_read, installOffset, buf, toRead); - FSFILE_Write(ciaHandle, &bytes_written, installOffset, buf, toRead, FS_WRITE_FLUSH); - installOffset += bytes_read; - } while(installOffset < installSize); - delete[] buf; + installSize = size; + do { + FSFILE_Read(fileHandle, &bytes_read, installOffset, buf, toRead); + FSFILE_Write(ciaHandle, &bytes_written, installOffset, buf, toRead, FS_WRITE_FLUSH); + installOffset += bytes_read; + } while(installOffset < installSize); + delete[] buf; - ret = AM_FinishCiaInstall(ciaHandle); - if (R_FAILED(ret)) { - printf("Error in:\nAM_FinishCiaInstall\n"); - return ret; + ret = AM_FinishCiaInstall(ciaHandle); + if (R_FAILED(ret)) { + printf("Error in:\nAM_FinishCiaInstall\n"); + FSFILE_Close(fileHandle); + return ret; + } } ret = FSFILE_Close(fileHandle); diff --git a/source/utils/download.cpp b/source/utils/download.cpp index 8648744..7954e33 100644 --- a/source/utils/download.cpp +++ b/source/utils/download.cpp @@ -103,11 +103,14 @@ static void commitToFileThreadFunc(void *args) { } static size_t file_handle_data(char *ptr, size_t size, size_t nmemb, void *userdata) { + if (getAvailableSpace() < (u64)downloadTotal) return 0; // Out of space. + if (writeError) return 0; + if (QueueSystem::CancelCallback) return 0; + (void)userdata; const size_t bsz = size * nmemb; size_t tofill = 0; - if (writeError) return 0; - if (QueueSystem::CancelCallback) return 0; + if (!g_buffers[g_index]) { LightEvent_Init(&waitCommit, RESET_STICKY); @@ -593,83 +596,85 @@ bool DownloadUniStore(const std::string &URL, int currentRev, std::string &fl, b return false; } - if (nlohmann::json::accept(result_buf)) { - nlohmann::json parsedAPI = nlohmann::json::parse(result_buf); + if (getAvailableSpace() >= result_written) { + if (nlohmann::json::accept(result_buf)) { + nlohmann::json parsedAPI = nlohmann::json::parse(result_buf); - if (parsedAPI.contains("storeInfo") && parsedAPI.contains("storeContent")) { - /* Ensure, version == _UNISTORE_VERSION. */ - if (parsedAPI["storeInfo"].contains("version") && parsedAPI["storeInfo"]["version"].is_number()) { - if (parsedAPI["storeInfo"]["version"] == 3 || parsedAPI["storeInfo"]["version"] == 4) { - if (currentRev > -1) { + if (parsedAPI.contains("storeInfo") && parsedAPI.contains("storeContent")) { + /* Ensure, version == _UNISTORE_VERSION. */ + if (parsedAPI["storeInfo"].contains("version") && parsedAPI["storeInfo"]["version"].is_number()) { + if (parsedAPI["storeInfo"]["version"] == 3 || parsedAPI["storeInfo"]["version"] == 4) { + if (currentRev > -1) { - if (parsedAPI["storeInfo"].contains("revision") && parsedAPI["storeInfo"]["revision"].is_number()) { - const int rev = parsedAPI["storeInfo"]["revision"]; + if (parsedAPI["storeInfo"].contains("revision") && parsedAPI["storeInfo"]["revision"].is_number()) { + const int rev = parsedAPI["storeInfo"]["revision"]; - if (rev > currentRev) { - Msg::DisplayMsg(Lang::get("UPDATING_UNISTORE")); - if (parsedAPI["storeInfo"].contains("file") && parsedAPI["storeInfo"]["file"].is_string()) { - fl = parsedAPI["storeInfo"]["file"]; + if (rev > currentRev) { + Msg::DisplayMsg(Lang::get("UPDATING_UNISTORE")); + if (parsedAPI["storeInfo"].contains("file") && parsedAPI["storeInfo"]["file"].is_string()) { + fl = parsedAPI["storeInfo"]["file"]; - /* Make sure it's not "/", otherwise it breaks. */ - if (!(fl.find("/") != std::string::npos)) { + /* Make sure it's not "/", otherwise it breaks. */ + if (!(fl.find("/") != std::string::npos)) { - FILE *out = fopen((std::string(_STORE_PATH) + fl).c_str(), "w"); - fwrite(result_buf, sizeof(char), result_written, out); - fclose(out); + FILE *out = fopen((std::string(_STORE_PATH) + fl).c_str(), "w"); + fwrite(result_buf, sizeof(char), result_written, out); + fclose(out); - socExit(); - free(result_buf); - free(socubuf); - result_buf = nullptr; - result_sz = 0; - result_written = 0; + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + result_sz = 0; + result_written = 0; - return true; + return true; - } else { - Msg::waitMsg(Lang::get("FILE_SLASH")); + } else { + Msg::waitMsg(Lang::get("FILE_SLASH")); + } } } } + + } else { + if (parsedAPI["storeInfo"].contains("file") && parsedAPI["storeInfo"]["file"].is_string()) { + fl = parsedAPI["storeInfo"]["file"]; + + /* Make sure it's not "/", otherwise it breaks. */ + if (!(fl.find("/") != std::string::npos)) { + + FILE *out = fopen((std::string(_STORE_PATH) + fl).c_str(), "w"); + fwrite(result_buf, sizeof(char), result_written, out); + fclose(out); + + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + result_sz = 0; + result_written = 0; + + return true; + + } else { + Msg::waitMsg(Lang::get("FILE_SLASH")); + } + } } - } else { - if (parsedAPI["storeInfo"].contains("file") && parsedAPI["storeInfo"]["file"].is_string()) { - fl = parsedAPI["storeInfo"]["file"]; + } else if (parsedAPI["storeInfo"]["version"] < 3) { + Msg::waitMsg(Lang::get("UNISTORE_TOO_OLD")); - /* Make sure it's not "/", otherwise it breaks. */ - if (!(fl.find("/") != std::string::npos)) { + } else if (parsedAPI["storeInfo"]["version"] > _UNISTORE_VERSION) { + Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW")); - FILE *out = fopen((std::string(_STORE_PATH) + fl).c_str(), "w"); - fwrite(result_buf, sizeof(char), result_written, out); - fclose(out); - - socExit(); - free(result_buf); - free(socubuf); - result_buf = nullptr; - result_sz = 0; - result_written = 0; - - return true; - - } else { - Msg::waitMsg(Lang::get("FILE_SLASH")); - } - } } - - } else if (parsedAPI["storeInfo"]["version"] < 3) { - Msg::waitMsg(Lang::get("UNISTORE_TOO_OLD")); - - } else if (parsedAPI["storeInfo"]["version"] > _UNISTORE_VERSION) { - Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW")); - } - } - } else { - Msg::waitMsg(Lang::get("UNISTORE_INVALID_ERROR")); + } else { + Msg::waitMsg(Lang::get("UNISTORE_INVALID_ERROR")); + } } } @@ -733,23 +738,25 @@ bool DownloadSpriteSheet(const std::string &URL, const std::string &file) { return false; } - C2D_SpriteSheet sheet = C2D_SpriteSheetLoadFromMem(result_buf, result_written); + if (getAvailableSpace() >= result_written) { + C2D_SpriteSheet sheet = C2D_SpriteSheetLoadFromMem(result_buf, result_written); - if (sheet) { - if (C2D_SpriteSheetCount(sheet) > 0) { - FILE *out = fopen((std::string(_STORE_PATH) + file).c_str(), "w"); - fwrite(result_buf, sizeof(char), result_written, out); - fclose(out); + if (sheet) { + if (C2D_SpriteSheetCount(sheet) > 0) { + FILE *out = fopen((std::string(_STORE_PATH) + file).c_str(), "w"); + fwrite(result_buf, sizeof(char), result_written, out); + fclose(out); - socExit(); - free(result_buf); - free(socubuf); - result_buf = nullptr; - result_sz = 0; - result_written = 0; + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + result_sz = 0; + result_written = 0; - C2D_SpriteSheetFree(sheet); - return true; + C2D_SpriteSheetFree(sheet); + return true; + } } } diff --git a/source/utils/extract.cpp b/source/utils/extract.cpp index 81ac35b..dad2860 100644 --- a/source/utils/extract.cpp +++ b/source/utils/extract.cpp @@ -25,6 +25,7 @@ */ #include "extract.hpp" +#include "files.hpp" #include "queueSystem.hpp" #include "scriptUtils.hpp" #include @@ -65,6 +66,8 @@ Result getExtractedSize(const std::string &archivePath, const std::string &wante } Result extractArchive(const std::string &archivePath, const std::string &wantedFile, const std::string &outputPath) { + if (getAvailableSpace() < extractSize) return -1; // Out of space. + archive *a = archive_read_new(); archive_entry *entry; diff --git a/source/utils/fileBrowse.cpp b/source/utils/fileBrowse.cpp index 9d393fa..5f98e2f 100644 --- a/source/utils/fileBrowse.cpp +++ b/source/utils/fileBrowse.cpp @@ -25,6 +25,7 @@ */ #include "fileBrowse.hpp" +#include "files.hpp" #include "json.hpp" #include "structs.hpp" #include <3ds.h> @@ -236,6 +237,11 @@ int fcopy(const char *sourcePath, const char *destinationPath) { return -1; } + if(getAvailableSpace() < copySize) { + fclose(sourceFile); + return -1; + } + FILE *destinationFile = fopen(destinationPath, "wb"); if (!destinationFile) { fclose(sourceFile); @@ -245,7 +251,15 @@ int fcopy(const char *sourcePath, const char *destinationPath) { while(1) { /* Copy file to destination path. */ int numr = fread(copyBuf, sizeof(u32), copyBufSize, sourceFile); - fwrite(copyBuf, sizeof(u32), numr, destinationFile); + int written = fwrite(copyBuf, sizeof(u32), numr, destinationFile); + + if(written != numr) { + fclose(sourceFile); + fclose(destinationFile); + + return -1; + } + copyOffset += copyBufSize * sizeof(u32); if (copyOffset > copySize) { diff --git a/source/utils/files.cpp b/source/utils/files.cpp index fd34c6c..9f674e4 100644 --- a/source/utils/files.cpp +++ b/source/utils/files.cpp @@ -25,6 +25,8 @@ */ #include "files.hpp" +#include +#include FS_Path getPathInfo(const char *path, FS_ArchiveID *archive) { *archive = ARCHIVE_SDMC; @@ -127,4 +129,13 @@ Result removeDirRecursive(const char *path) { FSUSER_CloseArchive(archive); return ret; +} + +/* Code borrowed from GodMode9i: + https://github.com/DS-Homebrew/GodMode9i/blob/d68ac105e68b4a1fc2c706a08c7a394255c325c2/arm9/source/driveOperations.cpp#L166-L170 +*/ +u64 getAvailableSpace() { + struct statvfs st; + statvfs("sdmc:/", &st); + return (u64)st.f_bsize * (u64)st.f_bavail; } \ No newline at end of file diff --git a/source/utils/stringutils.cpp b/source/utils/stringutils.cpp index c8cb163..bd55f10 100644 --- a/source/utils/stringutils.cpp +++ b/source/utils/stringutils.cpp @@ -65,14 +65,15 @@ std::string StringUtils::FetchStringsFromVector(const std::vector & /* adapted from GM9i's byte parsing. */ -std::string StringUtils::formatBytes(int bytes) { +std::string StringUtils::formatBytes(u64 bytes) { char out[32]; - if (bytes == 1) snprintf(out, sizeof(out), "%d Byte", bytes); - else if (bytes < 1024) snprintf(out, sizeof(out), "%d Bytes", bytes); - else if (bytes < 1024 * 1024) snprintf(out, sizeof(out), "%.1f KiB", (float)bytes / 1024); - else if (bytes < 1024 * 1024 * 1024) snprintf(out, sizeof(out), "%.1f MiB", (float)bytes / 1024 / 1024); - else snprintf(out, sizeof(out), "%.1f GiB", (float)bytes / 1024 / 1024 / 1024); + if (bytes == 1) snprintf(out, sizeof(out), "%lld Byte", bytes); + else if (bytes < 1ull << 10) snprintf(out, sizeof(out), "%lld Bytes", bytes); + else if (bytes < 1ull << 20) snprintf(out, sizeof(out), "%.1f KiB", (float)bytes / 1024); + else if (bytes < 1ull << 30) snprintf(out, sizeof(out), "%.1f MiB", (float)bytes / 1024 / 1024); + else if (bytes < 1ull << 40) snprintf(out, sizeof(out), "%.1f GiB", (float)bytes / 1024 / 1024 / 1024); + else snprintf(out, sizeof(out), "%.1f TiB", (float)bytes / 1024 / 1024 / 1024 / 1024); return out; }