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>
This commit is contained in:
parent
bfecbc86af
commit
baa14561eb
11 changed files with 145 additions and 105 deletions
|
|
@ -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
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
namespace StringUtils {
|
||||
std::string lower_case(const std::string &str);
|
||||
std::string FetchStringsFromVector(const std::vector<std::string> &fetch);
|
||||
std::string formatBytes(int bytes);
|
||||
std::string formatBytes(u64 bytes);
|
||||
std::string GetMarkString(int marks);
|
||||
std::vector<std::string> GetMarks(int marks);
|
||||
std::string format(const char *fmt_str, ...);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "files.hpp"
|
||||
#include "storeUtils.hpp"
|
||||
#include "structs.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include "extract.hpp"
|
||||
#include "files.hpp"
|
||||
#include "queueSystem.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include <archive.hpp>
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
*/
|
||||
|
||||
#include "files.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -65,14 +65,15 @@ std::string StringUtils::FetchStringsFromVector(const std::vector<std::string> &
|
|||
/*
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue