save: Fix remap init, add graceful fail paths

This commit is contained in:
shchmue 2020-04-15 16:22:23 -06:00
parent 25ff127404
commit 8a742a45d4
3 changed files with 51 additions and 40 deletions

View File

@ -780,7 +780,7 @@ get_titlekeys:
save_ctx = calloc(1, sizeof(save_ctx_t)); save_ctx = calloc(1, sizeof(save_ctx_t));
u8 M[0x100]; u8 M[0x100];
if (f_open(&fp, "emmc:/save/80000000000000E1", FA_READ | FA_OPEN_EXISTING)) { if (f_open(&fp, "emmc:/save/80000000000000E1", FA_READ | FA_OPEN_EXISTING)) {
EPRINTF("Unable to open ES save 1. Skipping."); EPRINTF("Unable to open e1 save. Skipping.");
free(buffer); free(buffer);
goto dismount; goto dismount;
} }
@ -791,7 +791,9 @@ get_titlekeys:
save_ctx->file = &fp; save_ctx->file = &fp;
save_ctx->tool_ctx.action = 0; save_ctx->tool_ctx.action = 0;
memcpy(save_ctx->save_mac_key, save_mac_key, 0x10); memcpy(save_ctx->save_mac_key, save_mac_key, 0x10);
save_process(save_ctx); if (!save_process(save_ctx)) {
EPRINTF("Failed to process e1 save.");
}
char ticket_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket.bin"; char ticket_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket.bin";
char ticket_list_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket_list.bin"; char ticket_list_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket_list.bin";
@ -813,7 +815,7 @@ get_titlekeys:
} }
} }
if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) { if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) {
EPRINTF("Unable to locate ticket.bin in e1."); EPRINTF("Unable to locate ticket.bin in e1 save.");
goto dismount; goto dismount;
} }
save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block); save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block);
@ -852,7 +854,7 @@ get_titlekeys:
u32 common_titlekey_count = _titlekey_count; u32 common_titlekey_count = _titlekey_count;
if (f_open(&fp, "emmc:/save/80000000000000E2", FA_READ | FA_OPEN_EXISTING)) { if (f_open(&fp, "emmc:/save/80000000000000E2", FA_READ | FA_OPEN_EXISTING)) {
EPRINTF("Unable to open ES save 2. Skipping."); EPRINTF("Unable to open e2 save. Skipping.");
free(buffer); free(buffer);
goto dismount; goto dismount;
} }
@ -860,10 +862,12 @@ get_titlekeys:
save_ctx->file = &fp; save_ctx->file = &fp;
save_ctx->tool_ctx.action = 0; save_ctx->tool_ctx.action = 0;
memcpy(save_ctx->save_mac_key, save_mac_key, 0x10); memcpy(save_ctx->save_mac_key, save_mac_key, 0x10);
save_process(save_ctx); if (!save_process(save_ctx)) {
EPRINTF("Failed to process e1 save.");
}
if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_list_bin_path, &entry)) { if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_list_bin_path, &entry)) {
EPRINTF("Unable to locate ticket_list.bin in e2."); EPRINTF("Unable to locate ticket_list.bin in e2 save.");
goto dismount; goto dismount;
} }
save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block); save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block);
@ -881,7 +885,7 @@ get_titlekeys:
} }
} }
if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) { if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) {
EPRINTF("Unable to locate ticket.bin in e2."); EPRINTF("Unable to locate ticket.bin in e2 save.");
goto dismount; goto dismount;
} }

View File

@ -70,27 +70,27 @@ uint32_t save_duplex_storage_read(duplex_storage_ctx_t *ctx, void *buffer, uint6
} }
remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, remap_entry_ctx_t *map_entries, uint32_t num_map_entries) { remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, remap_entry_ctx_t *map_entries, uint32_t num_map_entries) {
remap_segment_ctx_t *segments = malloc(sizeof(remap_segment_ctx_t) * header->map_segment_count); remap_segment_ctx_t *segments = calloc(1, sizeof(remap_segment_ctx_t) * header->map_segment_count);
unsigned int entry_idx = 0; unsigned int entry_idx = 0;
for (unsigned int i = 0; i < header->map_segment_count; i++) { for (unsigned int i = 0; i < header->map_segment_count; i++) {
remap_segment_ctx_t *seg = &segments[i]; remap_segment_ctx_t *seg = &segments[i];
seg->entries = malloc(sizeof(remap_entry_ctx_t)); seg->entry_count = 0;
memcpy(seg->entries, &map_entries[entry_idx], sizeof(remap_entry_ctx_t)); seg->entries = malloc(sizeof(remap_entry_ctx_t *));
seg->entries[seg->entry_count++] = &map_entries[entry_idx];
seg->offset = map_entries[entry_idx].virtual_offset; seg->offset = map_entries[entry_idx].virtual_offset;
map_entries[entry_idx].segment = seg; map_entries[entry_idx++].segment = seg;
seg->entry_count = 1;
entry_idx++;
while (entry_idx < num_map_entries && map_entries[entry_idx - 1].virtual_offset_end == map_entries[entry_idx].virtual_offset) { while (entry_idx < num_map_entries && map_entries[entry_idx - 1].virtual_offset_end == map_entries[entry_idx].virtual_offset) {
map_entries[entry_idx].segment = seg; map_entries[entry_idx].segment = seg;
map_entries[entry_idx - 1].next = &map_entries[entry_idx]; map_entries[entry_idx - 1].next = &map_entries[entry_idx];
seg->entries = malloc(sizeof(remap_entry_ctx_t)); remap_entry_ctx_t **ptr = calloc(1, sizeof(remap_entry_ctx_t *) * (seg->entry_count + 1));
memcpy(seg->entries, &map_entries[entry_idx], sizeof(remap_entry_ctx_t)); memcpy(ptr, seg->entries, sizeof(remap_entry_ctx_t *) * (seg->entry_count));
seg->entry_count++; free(seg->entries);
entry_idx++; seg->entries = ptr;
seg->entries[seg->entry_count++] = &map_entries[entry_idx++];
} }
seg->length = seg->entries[seg->entry_count - 1].virtual_offset_end - seg->entries[0].virtual_offset; seg->length = seg->entries[seg->entry_count - 1]->virtual_offset_end - seg->entries[0]->virtual_offset;
} }
return segments; return segments;
} }
@ -99,8 +99,8 @@ remap_entry_ctx_t *save_remap_get_map_entry(remap_storage_ctx_t *ctx, uint64_t o
uint32_t segment_idx = (uint32_t)(offset >> (64 - ctx->header->segment_bits)); uint32_t segment_idx = (uint32_t)(offset >> (64 - ctx->header->segment_bits));
if (segment_idx < ctx->header->map_segment_count) { if (segment_idx < ctx->header->map_segment_count) {
for (unsigned int i = 0; i < ctx->segments[segment_idx].entry_count; i++) for (unsigned int i = 0; i < ctx->segments[segment_idx].entry_count; i++)
if (ctx->segments[segment_idx].entries[i].virtual_offset_end > offset) if (ctx->segments[segment_idx].entries[i]->virtual_offset_end > offset)
return &ctx->segments[segment_idx].entries[i]; return ctx->segments[segment_idx].entries[i];
} }
return NULL; return NULL;
} }
@ -179,7 +179,7 @@ void save_ivfc_storage_init(hierarchical_integrity_verification_storage_ctx_t *c
uint32_t length; uint32_t length;
}; };
static struct salt_source_t salt_sources[6] = { static const struct salt_source_t salt_sources[6] = {
{"HierarchicalIntegrityVerificationStorage::Master", 48}, {"HierarchicalIntegrityVerificationStorage::Master", 48},
{"HierarchicalIntegrityVerificationStorage::L1", 44}, {"HierarchicalIntegrityVerificationStorage::L1", 44},
{"HierarchicalIntegrityVerificationStorage::L2", 44}, {"HierarchicalIntegrityVerificationStorage::L2", 44},
@ -238,12 +238,14 @@ size_t save_ivfc_level_fread(ivfc_level_save_ctx_t *ctx, void *buffer, uint64_t
void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buffer, uint64_t offset, size_t count, uint32_t verify) { void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buffer, uint64_t offset, size_t count, uint32_t verify) {
if (count > ctx->sector_size) { if (count > ctx->sector_size) {
EPRINTF("IVFC read exceeds sector size!\n"); EPRINTF("IVFC read exceeds sector size!\n");
return;
} }
uint64_t block_index = offset / ctx->sector_size; uint64_t block_index = offset / ctx->sector_size;
if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) { if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) {
EPRINTFARGS("Hash error from previous check\n found at offset %x count %x!\n", (u32)offset, count); EPRINTFARGS("Hash error from previous check\n found at offset %x count %x!\n", (u32)offset, count);
return;
} }
uint8_t hash_buffer[0x20] = {0}; uint8_t hash_buffer[0x20] = {0};
@ -270,7 +272,7 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf
uint8_t hash[0x20] = {0}; uint8_t hash[0x20] = {0};
uint8_t *data_buffer = calloc(1, ctx->sector_size + 0x20); uint8_t *data_buffer = calloc(1, ctx->sector_size + 0x20);
memcpy(data_buffer, ctx->salt, 0x20); memcpy(data_buffer, ctx->salt, 0x20);
memcpy(data_buffer + 0x20, buffer, count); memcpy(data_buffer + 0x20, buffer, ctx->sector_size);
se_calc_sha256(hash, data_buffer, ctx->sector_size + 0x20); se_calc_sha256(hash, data_buffer, ctx->sector_size + 0x20);
hash[0x1F] |= 0x80; hash[0x1F] |= 0x80;
@ -284,6 +286,7 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf
if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) { if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) {
EPRINTFARGS("Hash error from current check\n found at offset %x count %x!\n", (u32)offset, count); EPRINTFARGS("Hash error from current check\n found at offset %x count %x!\n", (u32)offset, count);
return;
} }
} }
@ -295,6 +298,7 @@ uint32_t save_allocation_table_read_entry_with_length(allocation_table_ctx_t *ct
if ((entries[0].next & 0x80000000) == 0) { if ((entries[0].next & 0x80000000) == 0) {
if (entries[0].prev & 0x80000000 && entries[0].prev != 0x80000000) { if (entries[0].prev & 0x80000000 && entries[0].prev != 0x80000000) {
EPRINTF("Invalid range entry in allocation table!\n"); EPRINTF("Invalid range entry in allocation table!\n");
return 0;
} }
} else { } else {
length = entries[1].next - entry_index + 1; length = entries[1].next - entry_index + 1;
@ -354,6 +358,7 @@ void save_allocation_table_iterator_begin(allocation_table_iterator_ctx_t *ctx,
if (ctx->prev_block != 0xFFFFFFFF) { if (ctx->prev_block != 0xFFFFFFFF) {
EPRINTFARGS("Attempted to start FAT iteration from\n invalid block %x!\n", initial_block); EPRINTFARGS("Attempted to start FAT iteration from\n invalid block %x!\n", initial_block);
return;
} }
} }
@ -464,6 +469,8 @@ uint32_t save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_
while (index) { while (index) {
if (index > capacity) { if (index > capacity) {
EPRINTFARGS("Save entry index %d out of range!", index); EPRINTFARGS("Save entry index %d out of range!", index);
*prev_index = 0xFFFFFFFF;
return 0xFFFFFFFF;
} }
save_fs_list_read_entry(ctx, index, &entry); save_fs_list_read_entry(ctx, index, &entry);
if (entry.parent == key->parent && !strcmp(entry.name, key->name)) { if (entry.parent == key->parent && !strcmp(entry.name, key->name)) {
@ -619,26 +626,25 @@ validity_t save_filesystem_verify(save_ctx_t *ctx) {
return journal_validity; return journal_validity;
} }
void save_process(save_ctx_t *ctx) { bool save_process(save_ctx_t *ctx) {
/* Try to parse Header A. */ /* Try to parse Header A. */
f_lseek(ctx->file, 0); f_lseek(ctx->file, 0);
if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) { if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) {
EPRINTF("Failed to read save header!\n"); EPRINTF("Failed to read save header!\n");
return false;
} }
save_process_header(ctx); if (!save_process_header(ctx) || (ctx->header_hash_validity == VALIDITY_INVALID)) {
if (ctx->header_hash_validity == VALIDITY_INVALID) {
/* Try to parse Header B. */ /* Try to parse Header B. */
f_lseek(ctx->file, 0x4000); f_lseek(ctx->file, 0x4000);
if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) { if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) {
EPRINTF("Failed to read save header!\n"); EPRINTF("Failed to read save header!\n");
return false;
} }
save_process_header(ctx); if (!save_process_header(ctx) || (ctx->header_hash_validity == VALIDITY_INVALID)) {
if (ctx->header_hash_validity == VALIDITY_INVALID) {
EPRINTF("Error: Save header is invalid!\n"); EPRINTF("Error: Save header is invalid!\n");
return false;
} }
} }
@ -759,14 +765,18 @@ void save_process(save_ctx_t *ctx) {
/* Initialize core save filesystem. */ /* Initialize core save filesystem. */
ctx->save_filesystem_core.base_storage = &ctx->core_data_ivfc_storage; ctx->save_filesystem_core.base_storage = &ctx->core_data_ivfc_storage;
save_filesystem_init(&ctx->save_filesystem_core, ctx->fat_storage, &ctx->header.save_header, &ctx->header.fat_header); save_filesystem_init(&ctx->save_filesystem_core, ctx->fat_storage, &ctx->header.save_header, &ctx->header.fat_header);
return true;
} }
void save_process_header(save_ctx_t *ctx) { bool save_process_header(save_ctx_t *ctx) {
if (ctx->header.layout.magic != MAGIC_DISF || ctx->header.duplex_header.magic != MAGIC_DPFS || if (ctx->header.layout.magic != MAGIC_DISF || ctx->header.duplex_header.magic != MAGIC_DPFS ||
ctx->header.data_ivfc_header.magic != MAGIC_IVFC || ctx->header.journal_header.magic != MAGIC_JNGL || ctx->header.data_ivfc_header.magic != MAGIC_IVFC || ctx->header.journal_header.magic != MAGIC_JNGL ||
ctx->header.save_header.magic != MAGIC_SAVE || ctx->header.main_remap_header.magic != MAGIC_RMAP || ctx->header.save_header.magic != MAGIC_SAVE || ctx->header.main_remap_header.magic != MAGIC_RMAP ||
ctx->header.meta_remap_header.magic != MAGIC_RMAP) { ctx->header.meta_remap_header.magic != MAGIC_RMAP)
{
EPRINTF("Error: Save header is corrupt!\n"); EPRINTF("Error: Save header is corrupt!\n");
return false;
} }
ctx->data_ivfc_master = (uint8_t *)&ctx->header + ctx->header.layout.ivfc_master_hash_offset_a; ctx->data_ivfc_master = (uint8_t *)&ctx->header + ctx->header.layout.ivfc_master_hash_offset_a;
@ -781,19 +791,16 @@ void save_process_header(save_ctx_t *ctx) {
if (ctx->header.layout.version >= 0x50000) { if (ctx->header.layout.version >= 0x50000) {
ctx->header.fat_ivfc_header.num_levels = 4; ctx->header.fat_ivfc_header.num_levels = 4;
} }
return true;
} }
void save_free_contexts(save_ctx_t *ctx) { void save_free_contexts(save_ctx_t *ctx) {
for (unsigned int i = 0; i < ctx->data_remap_storage.header->map_segment_count; i++) { for (unsigned int i = 0; i < ctx->data_remap_storage.header->map_segment_count; i++) {
for (unsigned int j = 0; j < ctx->data_remap_storage.segments[i].entry_count; j++) { free(ctx->data_remap_storage.segments[i].entries);
free(&ctx->data_remap_storage.segments[i].entries[j]);
}
} }
free(ctx->data_remap_storage.segments); free(ctx->data_remap_storage.segments);
for (unsigned int i = 0; i < ctx->meta_remap_storage.header->map_segment_count; i++) { for (unsigned int i = 0; i < ctx->meta_remap_storage.header->map_segment_count; i++) {
for (unsigned int j = 0; j < ctx->meta_remap_storage.segments[i].entry_count; j++) { free(ctx->meta_remap_storage.segments[i].entries);
free(&ctx->meta_remap_storage.segments[i].entries[j]);
}
} }
free(ctx->meta_remap_storage.segments); free(ctx->meta_remap_storage.segments);
free(ctx->data_remap_storage.map_entries); free(ctx->data_remap_storage.map_entries);

View File

@ -157,7 +157,7 @@ struct remap_entry_ctx_t {
struct remap_segment_ctx_t{ struct remap_segment_ctx_t{
uint64_t offset; uint64_t offset;
uint64_t length; uint64_t length;
remap_entry_ctx_t *entries; remap_entry_ctx_t **entries;
uint64_t entry_count; uint64_t entry_count;
}; };
@ -472,8 +472,8 @@ static inline uint32_t save_allocation_table_get_free_list_block_index(allocatio
return allocation_table_entry_index_to_block(save_allocation_table_get_free_list_entry_index(ctx)); return allocation_table_entry_index_to_block(save_allocation_table_get_free_list_entry_index(ctx));
} }
void save_process(save_ctx_t *ctx); bool save_process(save_ctx_t *ctx);
void save_process_header(save_ctx_t *ctx); bool save_process_header(save_ctx_t *ctx);
void save_save(save_ctx_t *ctx); void save_save(save_ctx_t *ctx);
void save_print(save_ctx_t *ctx); void save_print(save_ctx_t *ctx);