From 89994995afe11f07ab3b0fdc608cef70473b839d Mon Sep 17 00:00:00 2001 From: niklascfw Date: Sat, 14 Feb 2026 16:05:57 +0100 Subject: [PATCH] Add pre-update/clean backup feature - Backup deletion paths before update to sd:/backup/OmniNX/{version} - Backup deletion paths before clean install to sd:/backup/OmniNX/pre-omninx - Best-effort backup: continue install even if some copies fail - Fix directory creation: ensure parent dirs exist before folder_copy - Step 1 for update: Backup; Step 1-2 for clean: user data + pre-omninx backup Co-authored-by: Cursor --- source/backup.c | 83 +++++++++++++++++++++++++++++++++++++++++ source/backup.h | 5 +++ source/install.c | 50 +++++++++++++++++++++---- source/install.h | 2 + source/install_clean.c | 21 +++++++++++ source/install_update.c | 20 ++++++++++ 6 files changed, 174 insertions(+), 7 deletions(-) diff --git a/source/backup.c b/source/backup.c index d4ca5ad..ec06e3e 100644 --- a/source/backup.c +++ b/source/backup.c @@ -6,14 +6,97 @@ #include "fs.h" #include #include +#include #include +#define BACKUP_PATH_MAX 270 + // Check if file/directory exists static bool path_exists(const char *path) { FILINFO fno; return (f_stat(path, &fno) == FR_OK); } +// Create all parent directories for a path (mkdir -p style) +static void ensure_parent_dirs(const char *full_path) { + char buf[BACKUP_PATH_MAX]; + int n = (int)strlen(full_path); + if (n >= BACKUP_PATH_MAX) return; + memcpy(buf, full_path, n + 1); + + for (int i = 4; i < n; i++) { + if (buf[i] == '/') { + buf[i] = '\0'; + f_mkdir(buf); + buf[i] = '/'; + } + } +} + +// Copy a single path (file or dir) to backup base, preserving SD structure +static int backup_single_path(const char *src_path, const char *backup_base) { + FILINFO fno; + if (f_stat(src_path, &fno) != FR_OK) + return FR_OK; + + const char *relative = src_path; + if (strncmp(relative, "sd:/", 4) == 0) + relative += 4; + + if (fno.fattrib & AM_DIR) { + const char *last_slash = strrchr(relative, '/'); + char parent[BACKUP_PATH_MAX]; + if (last_slash && last_slash > relative) { + s_printf(parent, "%s/%.*s", backup_base, (int)(last_slash - relative), relative); + /* Create full parent hierarchy (e.g. atmosphere, atmosphere/contents) */ + ensure_parent_dirs(parent); + f_mkdir(parent); /* Ensure parent exists; FR_EXIST is fine */ + return folder_copy(src_path, parent); + } else { + ensure_parent_dirs(backup_base); + return folder_copy(src_path, backup_base); + } + } else { + char dst_path[BACKUP_PATH_MAX]; + s_printf(dst_path, "%s/%s", backup_base, relative); + ensure_parent_dirs(dst_path); + return file_copy(src_path, dst_path); + } +} + +// Backup all paths from varargs lists (same structure as delete_path_lists_grouped) +int backup_deletion_lists(const char *backup_base, ...) { + va_list ap; + const char **list; + int res = FR_OK; + + /* FatFS f_mkdir only creates the last component; parent must exist first */ + ensure_parent_dirs(BACKUP_BASE_PATH); + res = f_mkdir(BACKUP_BASE_PATH); + if (res != FR_OK && res != FR_EXIST) + return res; + + ensure_parent_dirs(backup_base); + res = f_mkdir(backup_base); + if (res != FR_OK && res != FR_EXIST) + return res; + + va_start(ap, backup_base); + while ((list = va_arg(ap, const char **)) != NULL) { + for (int i = 0; list[i] != NULL; i++) { + if (path_exists(list[i])) { + int copy_res = backup_single_path(list[i], backup_base); + if (copy_res != FR_OK && res == FR_OK) + res = copy_res; // Remember first error but keep going + } + } + } + va_end(ap); + + /* Best-effort: return FR_OK so install continues even if some copies failed */ + return FR_OK; +} + // Backup user data before clean install int backup_user_data(void) { int res; diff --git a/source/backup.h b/source/backup.h index f5ad447..dcaa11f 100644 --- a/source/backup.h +++ b/source/backup.h @@ -7,6 +7,8 @@ #include #define TEMP_BACKUP_PATH "sd:/temp_backup" +#define BACKUP_BASE_PATH "sd:/backup/OmniNX" +#define PRE_OMNINX_LABEL "pre-omninx" // Backup user data (DBI, Tinfoil, prod.keys) before clean install int backup_user_data(void); @@ -16,3 +18,6 @@ int restore_user_data(void); // Clean up temporary backup directory int cleanup_backup(void); + +// Backup paths from varargs lists to backup_base (pass path arrays, terminate with NULL) +int backup_deletion_lists(const char *backup_base, ...); diff --git a/source/install.c b/source/install.c index 015b5b6..21f5452 100644 --- a/source/install.c +++ b/source/install.c @@ -591,9 +591,29 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { int res; if (mode == INSTALL_MODE_UPDATE) { - // Update mode: selective cleanup then install + // Update mode: backup, selective cleanup, then install + omninx_status_t status = detect_omninx_installation(); + const char *version = (status.version_file[0] != '\0') ? status.version_file : "unknown"; + install_set_color(COLOR_YELLOW); - gfx_printf("Schritt 1: Bereinigung...\n"); + gfx_printf("Schritt 1: Backup vor Update (%s)...\n", version); + install_set_color(COLOR_WHITE); + res = backup_before_update(version); + /* Backup is best-effort; continue install even on failure */ + if (res == FR_OK) { + install_set_color(COLOR_GREEN); + gfx_printf(" [OK] Sicherung nach sd:/backup/OmniNX/%s/\n", version); + install_set_color(COLOR_WHITE); + } else { + install_set_color(COLOR_ORANGE); + gfx_printf(" [WARN] Backup fehlgeschlagen, fahre trotzdem fort...\n"); + install_set_color(COLOR_WHITE); + } + + install_check_and_clear_screen_if_needed(); + gfx_printf("\n"); + install_set_color(COLOR_YELLOW); + gfx_printf("Schritt 2: Bereinigung...\n"); install_set_color(COLOR_WHITE); res = update_mode_cleanup(pack_variant); if (res != FR_OK) return res; @@ -601,7 +621,7 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); - gfx_printf("Schritt 2: Dateien kopieren...\n"); + gfx_printf("Schritt 3: Dateien kopieren...\n"); install_set_color(COLOR_WHITE); res = update_mode_install(pack_variant); if (res != FR_OK) return res; @@ -614,7 +634,7 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { res = cleanup_other_staging_directories(pack_variant); return res; } else { - // Clean mode: backup, wipe, restore, install + // Clean mode: backup user data, backup pre-omninx, wipe, restore, install install_set_color(COLOR_YELLOW); gfx_printf("Schritt 1: Sichere Benutzerdaten...\n"); install_set_color(COLOR_WHITE); @@ -624,7 +644,23 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); - gfx_printf("Schritt 2: Bereinige alte Installation...\n"); + gfx_printf("Schritt 2: Backup alter CFW-Dateien (pre-omninx)...\n"); + install_set_color(COLOR_WHITE); + res = backup_before_clean(); + if (res == FR_OK) { + install_set_color(COLOR_GREEN); + gfx_printf(" [OK] Sicherung nach sd:/backup/OmniNX/pre-omninx/\n"); + install_set_color(COLOR_WHITE); + } else { + install_set_color(COLOR_ORANGE); + gfx_printf(" [WARN] Backup fehlgeschlagen, fahre trotzdem fort...\n"); + install_set_color(COLOR_WHITE); + } + + install_check_and_clear_screen_if_needed(); + gfx_printf("\n"); + install_set_color(COLOR_YELLOW); + gfx_printf("Schritt 3: Bereinige alte Installation...\n"); install_set_color(COLOR_WHITE); res = clean_mode_wipe(); if (res != FR_OK) return res; @@ -632,7 +668,7 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); - gfx_printf("Schritt 3: Stelle Benutzerdaten wieder her...\n"); + gfx_printf("Schritt 4: Stelle Benutzerdaten wieder her...\n"); install_set_color(COLOR_WHITE); res = clean_mode_restore(); if (res != FR_OK) return res; @@ -640,7 +676,7 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); - gfx_printf("Schritt 4: Dateien kopieren...\n"); + gfx_printf("Schritt 5: Dateien kopieren...\n"); install_set_color(COLOR_WHITE); res = clean_mode_install(pack_variant); if (res != FR_OK) return res; diff --git a/source/install.h b/source/install.h index d1a86a2..66a465c 100644 --- a/source/install.h +++ b/source/install.h @@ -17,6 +17,7 @@ typedef enum { int perform_installation(omninx_variant_t pack_variant, install_mode_t mode); // Update mode operations (install_update.c) +int backup_before_update(const char *version); int update_mode_cleanup(omninx_variant_t variant); int update_mode_install(omninx_variant_t variant); int cleanup_staging_directory(omninx_variant_t pack_variant); @@ -24,6 +25,7 @@ int cleanup_staging_directory(omninx_variant_t pack_variant); int cleanup_other_staging_directories(omninx_variant_t installed_variant); // Clean install operations (install_clean.c) +int backup_before_clean(void); int clean_mode_backup(void); int clean_mode_wipe(void); int clean_mode_restore(void); diff --git a/source/install_clean.c b/source/install_clean.c index 40960b3..c372a7c 100644 --- a/source/install_clean.c +++ b/source/install_clean.c @@ -11,6 +11,7 @@ #include "gfx.h" #include "version.h" #include +#include #include #undef COLOR_CYAN @@ -31,6 +32,26 @@ #define path_exists install_path_exists #define count_directory_items install_count_directory_items +// Backup paths before clean install (sd:/backup/OmniNX/pre-omninx) +int backup_before_clean(void) { + char backup_base[270]; + s_printf(backup_base, "%s/%s", BACKUP_BASE_PATH, PRE_OMNINX_LABEL); + return backup_deletion_lists(backup_base, + clean_atmosphere_dirs_to_delete, + clean_atmosphere_contents_dirs_to_delete, + clean_atmosphere_files_to_delete, + clean_bootloader_dirs_to_delete, + clean_bootloader_files_to_delete, + clean_config_dirs_to_delete, + clean_switch_dirs_to_delete, + clean_switch_files_to_delete, + clean_root_files_to_delete, + clean_misc_dirs_to_delete, + clean_misc_files_to_delete, + old_version_files_to_delete, + NULL); +} + // Clean mode: Backup user data int clean_mode_backup(void) { set_color(COLOR_CYAN); diff --git a/source/install_update.c b/source/install_update.c index 3b1e662..efc5063 100644 --- a/source/install_update.c +++ b/source/install_update.c @@ -4,6 +4,7 @@ */ #include "install.h" +#include "backup.h" #include "deletion_lists_update.h" #include "fs.h" #include "version.h" @@ -34,6 +35,25 @@ #define path_exists install_path_exists #define count_directory_items install_count_directory_items +// Backup paths before update (sd:/backup/OmniNX/{version}) +int backup_before_update(const char *version) { + char backup_base[270]; + s_printf(backup_base, "%s/%s", BACKUP_BASE_PATH, version); + return backup_deletion_lists(backup_base, + atmosphere_dirs_to_delete, + atmosphere_contents_dirs_to_delete, + atmosphere_files_to_delete, + bootloader_dirs_to_delete, + bootloader_files_to_delete, + config_dirs_to_delete, + switch_dirs_to_delete, + switch_files_to_delete, + root_files_to_delete, + misc_dirs_to_delete, + misc_files_to_delete, + NULL); +} + // Update mode: Cleanup specific directories/files int update_mode_cleanup(omninx_variant_t variant) { (void)variant;