Pull out get file util

Update CMakeLists.txt
This commit is contained in:
Ben V. Brown 2023-08-01 17:41:10 +10:00
parent 9893a49376
commit d052d2ebe4
5 changed files with 271 additions and 258 deletions

View File

@ -13,6 +13,7 @@ add_library(file_parsers
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c" "${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c"
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c" "${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c"
"${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c" "${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c"
"${CMAKE_CURRENT_SOURCE_DIR}/get_file_contents.c"
) )
target_include_directories(file_parsers PUBLIC target_include_directories(file_parsers PUBLIC

View File

@ -41,8 +41,10 @@ enum prefix_type {
struct dfu_file parse_dfu_suffix(const uint8_t* file_contents, struct dfu_file parse_dfu_suffix(const uint8_t* file_contents,
size_t file_contents_length); size_t file_contents_length);
ssize_t parse_target(const uint8_t* data, uint8_t* out_ealt, ssize_t parse_target(const uint8_t* data,
uint8_t** out_data, size_t* out_data_size, uint8_t* out_ealt,
uint8_t** out_data,
size_t* out_data_size,
size_t* out_data_address); size_t* out_data_address);
ssize_t get_file_contents(const char* file_path_on_disk, ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents); uint8_t** file_contents);
@ -64,12 +66,12 @@ ssize_t get_file_contents(const char* file_path_on_disk,
* free(payload); * free(payload);
*/ */
int int dfu_file_parse(const char* file_path_on_disk,
dfu_file_parse(const char* file_path_on_disk, uint8_t** payload, uint8_t** payload,
size_t* payload_length, size_t* payload_address) { size_t* payload_length,
size_t* payload_address) {
uint8_t* dfu_file_contents = NULL; uint8_t* dfu_file_contents = NULL;
ssize_t file_size ssize_t file_size = get_file_contents(file_path_on_disk, &dfu_file_contents);
= get_file_contents(file_path_on_disk, &dfu_file_contents);
if (file_size <= 0 || dfu_file_contents == NULL) { if (file_size <= 0 || dfu_file_contents == NULL) {
return -1; return -1;
} }
@ -88,8 +90,8 @@ dfu_file_parse(const char* file_path_on_disk, uint8_t** payload,
uint8_t* blob = NULL; uint8_t* blob = NULL;
size_t blob_size = 0; size_t blob_size = 0;
size_t blob_address = 0; size_t blob_address = 0;
ssize_t res = parse_target(dfu_info.firmware + data_consumed, &ealt, ssize_t res = parse_target(dfu_info.firmware + data_consumed, &ealt, &blob,
&blob, &blob_size, &blob_address); &blob_size, &blob_address);
if (res < 0) { if (res < 0) {
break; break;
} }
@ -109,11 +111,13 @@ dfu_file_parse(const char* file_path_on_disk, uint8_t** payload,
} }
// Read next target, output data+size+alt. Returns bytes consumed // Read next target, output data+size+alt. Returns bytes consumed
ssize_t ssize_t parse_target(const uint8_t* data,
parse_target(const uint8_t* data, uint8_t* out_ealt, uint8_t** out_data, uint8_t* out_ealt,
size_t* out_data_size, size_t* out_data_address) { uint8_t** out_data,
if (data == NULL || out_ealt == NULL || out_data == NULL size_t* out_data_size,
|| out_data_size == NULL) { size_t* out_data_address) {
if (data == NULL || out_ealt == NULL || out_data == NULL ||
out_data_size == NULL) {
return -99; return -99;
} }
if (data[0] != 'T' || data[1] != 'a') { if (data[0] != 'T' || data[1] != 'a') {
@ -142,14 +146,13 @@ parse_target(const uint8_t* data, uint8_t* out_ealt, uint8_t** out_data,
return blob_length; return blob_length;
} }
static int static int probe_prefix(struct dfu_file* file) {
probe_prefix(struct dfu_file* file) {
const uint8_t* prefix = file->firmware; const uint8_t* prefix = file->firmware;
file->size.prefix = 0; file->size.prefix = 0;
if (file->size.total < LMDFU_PREFIX_LENGTH) if (file->size.total < LMDFU_PREFIX_LENGTH)
return 1; return 1;
if (prefix[0] == 'D' && prefix[1] == 'f' && prefix[2] == 'u' if (prefix[0] == 'D' && prefix[1] == 'f' && prefix[2] == 'u' &&
&& prefix[3] == 'S' && prefix[4] == 'e') { prefix[3] == 'S' && prefix[4] == 'e') {
// DfuSe header // DfuSe header
// https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/dfuse-pack.py#l110 // https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/dfuse-pack.py#l110
@ -159,11 +162,10 @@ probe_prefix(struct dfu_file* file) {
printf("Number DFU Targets: %d\n", numTargets); printf("Number DFU Targets: %d\n", numTargets);
} }
if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) {
uint32_t payload_length = (prefix[7] << 24) | (prefix[6] << 16) uint32_t payload_length =
| (prefix[5] << 8) | prefix[4]; (prefix[7] << 24) | (prefix[6] << 16) | (prefix[5] << 8) | prefix[4];
uint32_t expected_payload_length = (uint32_t)file->size.total uint32_t expected_payload_length =
- LMDFU_PREFIX_LENGTH (uint32_t)file->size.total - LMDFU_PREFIX_LENGTH - file->size.suffix;
- file->size.suffix;
if (payload_length != expected_payload_length) if (payload_length != expected_payload_length)
return 1; return 1;
file->prefix_type = LMDFU_PREFIX; file->prefix_type = LMDFU_PREFIX;
@ -179,8 +181,7 @@ probe_prefix(struct dfu_file* file) {
return 0; return 0;
} }
struct dfu_file struct dfu_file parse_dfu_suffix(const uint8_t* file_contents,
parse_dfu_suffix(const uint8_t* file_contents,
const size_t file_contents_length) { const size_t file_contents_length) {
// This is nearly 1:1 based on // This is nearly 1:1 based on
// https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/src/dfu_file.c#l368 // https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/src/dfu_file.c#l368
@ -203,8 +204,7 @@ parse_dfu_suffix(const uint8_t* file_contents,
dfu_suffix = file_contents + file_contents_length - DFU_SUFFIX_LENGTH; dfu_suffix = file_contents + file_contents_length - DFU_SUFFIX_LENGTH;
if (dfu_suffix[10] != 'D' || dfu_suffix[9] != 'F' if (dfu_suffix[10] != 'D' || dfu_suffix[9] != 'F' || dfu_suffix[8] != 'U') {
|| dfu_suffix[8] != 'U') {
reason = "Invalid DFU suffix signature"; reason = "Invalid DFU suffix signature";
missing_suffix = 1; missing_suffix = 1;
goto checked; goto checked;
@ -214,8 +214,8 @@ parse_dfu_suffix(const uint8_t* file_contents,
crc = crc32_byte(crc, file_contents[i]); crc = crc32_byte(crc, file_contents[i]);
} }
output.dwCRC = (dfu_suffix[15] << 24) + (dfu_suffix[14] << 16) output.dwCRC = (dfu_suffix[15] << 24) + (dfu_suffix[14] << 16) +
+ (dfu_suffix[13] << 8) + dfu_suffix[12]; (dfu_suffix[13] << 8) + dfu_suffix[12];
if (output.dwCRC != crc) { if (output.dwCRC != crc) {
reason = "DFU suffix CRC does not match"; reason = "DFU suffix CRC does not match";
@ -231,8 +231,7 @@ parse_dfu_suffix(const uint8_t* file_contents,
output.size.suffix = dfu_suffix[11]; output.size.suffix = dfu_suffix[11];
if (output.size.suffix < DFU_SUFFIX_LENGTH) { if (output.size.suffix < DFU_SUFFIX_LENGTH) {
fprintf(stderr, "Unsupported DFU suffix length %d", fprintf(stderr, "Unsupported DFU suffix length %d", output.size.suffix);
output.size.suffix);
} }
if (output.size.suffix > file_contents_length) { if (output.size.suffix > file_contents_length) {
@ -250,73 +249,26 @@ checked:
const uint8_t* data = file_contents; const uint8_t* data = file_contents;
if (output.prefix_type == DFUSE_PREFIX) { if (output.prefix_type == DFUSE_PREFIX) {
} else if (output.prefix_type == LMDFU_PREFIX) { } else if (output.prefix_type == LMDFU_PREFIX) {
printf("Possible TI Stellaris DFU prefix with " printf(
"Possible TI Stellaris DFU prefix with "
"the following properties\n" "the following properties\n"
"Address: 0x%08x\n" "Address: 0x%08x\n"
"Payload length: %d\n", "Payload length: %d\n",
output.lmdfu_address, output.lmdfu_address,
data[4] | (data[5] << 8) | (data[6] << 16) data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24));
| (data[7] << 24));
} else if (output.prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { } else if (output.prefix_type == LPCDFU_UNENCRYPTED_PREFIX) {
printf("Possible unencrypted NXP LPC DFU prefix with " printf(
"Possible unencrypted NXP LPC DFU prefix with "
"the following properties\n" "the following properties\n"
"Payload length: %d kiByte\n", "Payload length: %d kiByte\n",
data[2] >> 1 | (data[3] << 7)); data[2] >> 1 | (data[3] << 7));
} else { } else {
fprintf(stderr, "Unknown DFU prefix type"); fprintf(stderr, "Unknown DFU prefix type");
} }
output.firmware output.firmware =
= output.firmware + output.size.prefix; // shift past prefix output.firmware + output.size.prefix; // shift past prefix
} }
output.size.firmware output.size.firmware =
= output.size.total - (output.size.suffix + output.size.prefix); output.size.total - (output.size.suffix + output.size.prefix);
return output; return output;
} }
// Returns file size _or_ negative on error
ssize_t
get_file_contents(const char* file_path_on_disk, uint8_t** file_contents) {
size_t read_count;
size_t file_size = 0;
size_t read_total = 0;
FILE* f;
if (file_contents == NULL) {
return -99;
}
f = fopen(file_path_on_disk, "rb");
if (f <= 0) {
fprintf(stderr, "Could not open file %s for reading",
file_path_on_disk);
return -1;
}
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fseek(f, 0, SEEK_SET);
*file_contents = calloc(file_size, sizeof(uint8_t));
while (read_total < file_size) {
size_t to_read = file_size - read_total;
/* read() limit on Linux, slightly below MAX_INT on Windows */
if (to_read > 0x7ffff000)
to_read = 0x7ffff000;
read_count = fread((*file_contents) + read_total, 1, to_read, f);
if (read_count == 0)
break;
// If error and not end of file, break
if (read_count == -1 && !feof(f))
break;
read_total += read_count;
}
if (read_total != file_size) {
fprintf(stderr, "Could only read %lld of %lld bytes from %s",
(long long)read_total, (long long)file_size,
file_path_on_disk);
return -1;
}
fclose(f);
return (ssize_t)file_size;
}

View File

@ -0,0 +1,47 @@
#include <stdio.h>
#include "parse_file.h"
// Returns file size _or_ negative on error
ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents) {
size_t read_count;
size_t file_size = 0;
size_t read_total = 0;
FILE* f;
if (file_contents == NULL) {
return -99;
}
f = fopen(file_path_on_disk, "rb");
if (f <= 0) {
fprintf(stderr, "Could not open file %s for reading", file_path_on_disk);
return -1;
}
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fseek(f, 0, SEEK_SET);
*file_contents = calloc(file_size, sizeof(uint8_t));
while (read_total < file_size) {
size_t to_read = file_size - read_total;
/* read() limit on Linux, slightly below MAX_INT on Windows */
if (to_read > 0x7ffff000)
to_read = 0x7ffff000;
read_count = fread((*file_contents) + read_total, 1, to_read, f);
if (read_count == 0)
break;
// If error and not end of file, break
if (read_count == -1 && !feof(f))
break;
read_total += read_count;
}
if (read_total != file_size) {
fprintf(stderr, "Could only read %lld of %lld bytes from %s",
(long long)read_total, (long long)file_size, file_path_on_disk);
return -1;
}
fclose(f);
return (ssize_t)file_size;
}

View File

@ -1,5 +1,6 @@
#include "parse_file.h" #include "parse_file.h"
#include <string.h> #include <string.h>
#include "bin_file.h"
#include "dfu_file.h" #include "dfu_file.h"
const char* get_filename_ext(const char* filename) { const char* get_filename_ext(const char* filename) {
@ -13,17 +14,23 @@ int parse_firmware_file(const char* file_path_on_disk,
parsed_firmware_file_t* parsed_results) { parsed_firmware_file_t* parsed_results) {
// Switchcase on the extension of the file // Switchcase on the extension of the file
const char* ext = get_filename_ext(file_path_on_disk); const char* ext = get_filename_ext(file_path_on_disk);
int res = PARSED_ERROR_INVALID_FILETYPE;
if (strncmp(ext, "dfu", 3) == 0 || strncmp(ext, "DFU", 3) == 0) { if (strncmp(ext, "dfu", 3) == 0 || strncmp(ext, "DFU", 3) == 0) {
printf("Input file identified as a .dfu file\r\n"); printf("Input file identified as a .dfu file\r\n");
// Handle as a .dfu file // Handle as a .dfu file
return dfu_file_parse("test.dfu", &parsed_results->payload, res = dfu_file_parse("test.dfu", &parsed_results->payload,
&parsed_results->payload_length, &parsed_results->payload_length,
&parsed_results->payload_address); &parsed_results->payload_address);
} else if (strncmp(ext, "bin", 3) == 0 || strncmp(ext, "BIN", 3) == 0) { } else if (strncmp(ext, "bin", 3) == 0 || strncmp(ext, "BIN", 3) == 0) {
printf("Input file identified as a .bin file\r\n"); printf("Input file identified as a .bin file\r\n");
// Raw binary file // Raw binary file
res = bin_file_parse("test.dfu", &parsed_results->payload,
&parsed_results->payload_length,
&parsed_results->payload_address);
} }
// TODO: Hex files? // If we wanted to support hex files, here would be where
return PARSED_ERROR_INVALID_FILETYPE;
parsed_results->needs_boot_struct = parsed_results->payload_address == 0;
return res;
} }

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <stdio.h>
#include "parsed_firmware_file.h" #include "parsed_firmware_file.h"
#define PARSED_ERROR_INVALID_FILETYPE -0x1000 #define PARSED_ERROR_INVALID_FILETYPE -0x1000
#define PARSED_ERROR_CANT_OPEN_FILE -0x1001
#define PARSED_ERROR_TOO_BIG -0x1001 /* Input expands to be too big */ #define PARSED_ERROR_TOO_BIG -0x1001 /* Input expands to be too big */
#define PARSED_ERROR_BAD_DFU -0x1002 /* DFU file provided but not valid */ #define PARSED_ERROR_BAD_DFU -0x1002 /* DFU file provided but not valid */
@ -12,3 +14,7 @@
int parse_firmware_file(const char* file_path_on_disk, int parse_firmware_file(const char* file_path_on_disk,
parsed_firmware_file_t* parsed_results); parsed_firmware_file_t* parsed_results);
// Internal util
ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents);