[PATCH firmware-utils v1 07/10] tplink-safeloader: refactor image ingestion

Sander Vanheule sander at svanheule.net
Fri Feb 3 14:03:21 PST 2023


The tool supports three modes that ingest an existing safeloader image:
-i (image info), -x (extract payloads), and -z (convert to sysupgrade).
These modes all re-implement image parsing, so refactor the code to make
sure there is only one place this is performed.

Signed-off-by: Sander Vanheule <sander at svanheule.net>
---
 src/tplink-safeloader.c | 169 +++++++++++++++++++++++-----------------
 1 file changed, 98 insertions(+), 71 deletions(-)

diff --git a/src/tplink-safeloader.c b/src/tplink-safeloader.c
index 51f6683c802a..ab08ad1f8580 100644
--- a/src/tplink-safeloader.c
+++ b/src/tplink-safeloader.c
@@ -127,6 +127,12 @@ struct __attribute__((__packed__)) soft_version {
 	uint32_t compat_level;
 };
 
+/* Internal representation of safeloader image data */
+struct safeloader_image_info {
+	size_t payload_offset;
+	struct flash_partition_entry entries[MAX_PARTITIONS];
+};
+
 #define SAFELOADER_PREAMBLE_SIZE	0x14
 #define SAFELOADER_HEADER_SIZE		0x1000
 #define SAFELOADER_PAYLOAD_OFFSET	(SAFELOADER_PREAMBLE_SIZE + SAFELOADER_HEADER_SIZE)
@@ -3795,6 +3801,46 @@ static int read_partition_table(
 	return 0;
 }
 
+static void safeloader_read_partition(FILE *input_file, size_t payload_offset,
+				      struct flash_partition_entry *entry,
+				      struct image_partition_entry *part)
+{
+	size_t part_size = entry->size;
+	void *part_data = malloc(part_size);
+
+	if (fseek(input_file, payload_offset, SEEK_SET))
+		error(1, errno, "Failed to seek to partition data");
+
+	if (!part_data)
+		error(1, ENOMEM, "Failed to allocate partition data");
+
+	if (fread(part_data, 1, part_size, input_file) < part_size)
+		error(1, errno, "Failed to read partition data");
+
+	part->data = part_data;
+	part->size = part_size;
+	part->name = entry->name;
+}
+
+static void safeloader_parse_image(FILE *input_file, struct safeloader_image_info *image)
+{
+	char buf[64];
+
+	if (!input_file)
+		return;
+
+	fseek(input_file, SAFELOADER_PREAMBLE_SIZE, SEEK_SET);
+
+	if (fread(buf, sizeof(buf), 1, input_file) != 1)
+		error(1, errno, "Can not read image header");
+
+	image->payload_offset = SAFELOADER_PAYLOAD_OFFSET;
+
+	/* Parse image partition table */
+	read_partition_table(input_file, image->payload_offset, &image->entries[0],
+			     MAX_PARTITIONS, PARTITION_TABLE_FWUP);
+}
+
 static void write_partition(
 		FILE *input_file,
 		size_t firmware_offset,
@@ -3844,12 +3890,9 @@ static int extract_firmware_partition(FILE *input_file, size_t firmware_offset,
 /** extract all partitions from the firmware file */
 static int extract_firmware(const char *input, const char *output_directory)
 {
-	struct flash_partition_entry entries[16] = { 0 };
-	size_t max_entries = 16;
-	size_t firmware_offset = SAFELOADER_PAYLOAD_OFFSET;
-	FILE *input_file;
-
+	struct safeloader_image_info info = {};
 	struct stat statbuf;
+	FILE *input_file;
 
 	/* check input file */
 	if (stat(input, &statbuf)) {
@@ -3866,12 +3909,10 @@ static int extract_firmware(const char *input, const char *output_directory)
 	}
 
 	input_file = fopen(input, "rb");
+	safeloader_parse_image(input_file, &info);
 
-	if (read_partition_table(input_file, firmware_offset, entries, 16, PARTITION_TABLE_FWUP) != 0)
-		error(1, 0, "Error can not read the partition table (fwup-ptn)");
-
-	for (size_t i = 0; i < max_entries && entries[i].name; i++)
-		extract_firmware_partition(input_file, firmware_offset, &entries[i], output_directory);
+	for (size_t i = 0; i < MAX_PARTITIONS && info.entries[i].name; i++)
+		extract_firmware_partition(input_file, info.payload_offset, &info.entries[i], output_directory);
 
 	return 0;
 }
@@ -3894,39 +3935,33 @@ static struct flash_partition_entry *find_partition(
 
 static int firmware_info(const char *input)
 {
-	struct flash_partition_entry pointers[MAX_PARTITIONS] = { };
+	struct safeloader_image_info info = {};
+	struct image_partition_entry part = {};
 	struct flash_partition_entry *e;
-	FILE *fp;
-	int i;
+	FILE *input_file;
 
-	fp = fopen(input, "r");
+	input_file = fopen(input, "rb");
 
-	if (read_partition_table(fp, SAFELOADER_PAYLOAD_OFFSET, pointers, MAX_PARTITIONS, PARTITION_TABLE_FWUP))
-		error(1, 0, "Error can not read the partition table (fwup-ptn)");
+	safeloader_parse_image(input_file, &info);
 
 	printf("Firmware image partitions:\n");
 	printf("%-8s %-8s %s\n", "base", "size", "name");
 
-	e = &pointers[0];
-	for (i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
+	e = &info.entries[0];
+	for (unsigned int i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
 		printf("%08x %08x %s\n", e->base, e->size, e->name);
 
-	e = find_partition(pointers, MAX_PARTITIONS, "soft-version", NULL);
+	e = find_partition(&info.entries[0], MAX_PARTITIONS, "soft-version", NULL);
 	if (e) {
-		size_t data_len = e->size - sizeof(struct meta_header);
-		char *buf = malloc(data_len);
 		struct soft_version *s;
+		unsigned int ascii_len;
+		const uint8_t *buf;
+		size_t data_len;
 		bool isstr;
-		int i;
-
-		if (!buf)
-			error(1, errno, "Failed to alloc buffer");
 
-		if (fseek(fp, SAFELOADER_PAYLOAD_OFFSET + e->base + sizeof(struct meta_header), SEEK_SET))
-			error(1, errno, "Can not seek in the firmware");
-
-		if (fread(buf, data_len, 1, fp) != 1)
-			error(1, errno, "Can not read fwup-ptn data from the firmware");
+		safeloader_read_partition(input_file, info.payload_offset + e->base, e, &part);
+		data_len = ntohl(((struct meta_header *) part.data)->length);
+		buf = part.data + sizeof(struct meta_header);
 
 		/* Check for (null-terminated) string */
 		ascii_len = 0;
@@ -3937,10 +3972,10 @@ static int firmware_info(const char *input)
 
 		printf("\n[Software version]\n");
 		if (isstr) {
-			fwrite(buf, strnlen(buf, data_len), 1, stdout);
+			fwrite(buf, strnlen((const char *) buf, data_len), 1, stdout);
 			putchar('\n');
 		} else if (data_len >= offsetof(struct soft_version, rev)) {
-			s = (struct soft_version *)buf;
+			s = (struct soft_version *) buf;
 
 			printf("Version: %d.%d.%d\n", s->version_major, s->version_minor, s->version_patch);
 			printf("Date: %02x%02x-%02x-%02x\n", s->year_hi, s->year_lo, s->month, s->day);
@@ -3949,48 +3984,40 @@ static int firmware_info(const char *input)
 			printf("Failed to parse data\n");
 		}
 
-		free(buf);
+		free_image_partition(&part);
 	}
 
-	e = find_partition(pointers, MAX_PARTITIONS, "support-list", NULL);
+	e = find_partition(&info.entries[0], MAX_PARTITIONS, "support-list", NULL);
 	if (e) {
-		char buf[128];
-		size_t length;
-		size_t bytes;
-		size_t max_length = sizeof(buf) - 1;
+		size_t data_len;
 
-		if (fseek(fp, SAFELOADER_PAYLOAD_OFFSET + e->base + sizeof(struct meta_header), SEEK_SET))
-			error(1, errno, "Can not seek in the firmware");
+		safeloader_read_partition(input_file, info.payload_offset + e->base, e, &part);
+		data_len = ntohl(((struct meta_header *) part.data)->length);
 
 		printf("\n[Support list]\n");
-		for (length = e->size - sizeof(struct meta_header); length; length -= bytes) {
-			bytes = fread(buf, 1, length > max_length ? max_length: length, fp);
-			if (bytes <= 0)
-				error(1, errno, "Can not read fwup-ptn data from the firmware");
+		fwrite(part.data + sizeof(struct meta_header), data_len, 1, stdout);
+		printf("\n");
 
-			buf[bytes] = '\0';
-			printf(buf);
-		}
-        printf("\n");
+		free_image_partition(&part);
 	}
 
-	e = find_partition(pointers, MAX_PARTITIONS, "partition-table", NULL);
+	e = find_partition(&info.entries[0], MAX_PARTITIONS, "partition-table", NULL);
 	if (e) {
-		size_t flash_table_offset = SAFELOADER_PAYLOAD_OFFSET + e->base + 4;
-		struct flash_partition_entry parts[MAX_PARTITIONS] = { };
+		size_t flash_table_offset = info.payload_offset + e->base + 4;
+		struct flash_partition_entry parts[MAX_PARTITIONS] = {};
 
-		if (read_partition_table(fp, flash_table_offset, parts, MAX_PARTITIONS, PARTITION_TABLE_FLASH))
+		if (read_partition_table(input_file, flash_table_offset, parts, MAX_PARTITIONS, PARTITION_TABLE_FLASH))
 			error(1, 0, "Error can not read the partition table (partition)");
 
 		printf("\n[Partition table]\n");
 		printf("%-8s %-8s %s\n", "base", "size", "name");
 
 		e = &parts[0];
-		for (i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
+		for (unsigned int i = 0; i < MAX_PARTITIONS && e->name; i++, e++)
 			printf("%08x %08x %s\n", e->base, e->size, e->name);
 	}
 
-	fclose(fp);
+	fclose(input_file);
 
 	return 0;
 }
@@ -4017,16 +4044,17 @@ static void write_ff(FILE *output_file, size_t size)
 
 static void convert_firmware(const char *input, const char *output)
 {
-	struct flash_partition_entry fwup[MAX_PARTITIONS] = { 0 };
-	struct flash_partition_entry flash[MAX_PARTITIONS] = { 0 };
-	struct flash_partition_entry *fwup_os_image = NULL, *fwup_file_system = NULL;
-	struct flash_partition_entry *flash_os_image = NULL, *flash_file_system = NULL;
-	struct flash_partition_entry *fwup_partition_table = NULL;
-	size_t firmware_offset = SAFELOADER_PAYLOAD_OFFSET;
-	FILE *input_file, *output_file;
+	struct flash_partition_entry flash[MAX_PARTITIONS] = {};
+	struct flash_partition_entry *fwup_partition_table;
+	struct flash_partition_entry *flash_file_system;
+	struct flash_partition_entry *fwup_file_system;
+	struct flash_partition_entry *flash_os_image;
+	struct flash_partition_entry *fwup_os_image;
+	struct safeloader_image_info info = {};
 	size_t flash_table_offset;
-
 	struct stat statbuf;
+	FILE *output_file;
+	FILE *input_file;
 
 	/* check input file */
 	if (stat(input, &statbuf)) {
@@ -4041,19 +4069,18 @@ static void convert_firmware(const char *input, const char *output)
 	if (!output_file)
 		error(1, 0, "Can not open output firmware %s", output);
 
-	if (read_partition_table(input_file, firmware_offset, fwup, MAX_PARTITIONS, 0) != 0) {
-		error(1, 0, "Error can not read the partition table (fwup-ptn)");
-	}
+	input_file = fopen(input, "rb");
+	safeloader_parse_image(input_file, &info);
 
-	fwup_os_image = find_partition(fwup, MAX_PARTITIONS,
+	fwup_os_image = find_partition(info.entries, MAX_PARTITIONS,
 			"os-image", "Error can not find os-image partition (fwup)");
-	fwup_file_system = find_partition(fwup, MAX_PARTITIONS,
+	fwup_file_system = find_partition(info.entries, MAX_PARTITIONS,
 			"file-system", "Error can not find file-system partition (fwup)");
-	fwup_partition_table = find_partition(fwup, MAX_PARTITIONS,
+	fwup_partition_table = find_partition(info.entries, MAX_PARTITIONS,
 			"partition-table", "Error can not find partition-table partition");
 
 	/* the flash partition table has a 0x00000004 magic haeder */
-	flash_table_offset = firmware_offset + fwup_partition_table->base + 4;
+	flash_table_offset = info.payload_offset + fwup_partition_table->base + 4;
 	if (read_partition_table(input_file, flash_table_offset, flash, MAX_PARTITIONS, PARTITION_TABLE_FLASH) != 0)
 		error(1, 0, "Error can not read the partition table (flash)");
 
@@ -4063,12 +4090,12 @@ static void convert_firmware(const char *input, const char *output)
 			"file-system", "Error can not find file-system partition (flash)");
 
 	/* write os_image to 0x0 */
-	write_partition(input_file, firmware_offset, fwup_os_image, output_file);
+	write_partition(input_file, info.payload_offset, fwup_os_image, output_file);
 	write_ff(output_file, flash_os_image->size - fwup_os_image->size);
 
 	/* write file-system behind os_image */
 	fseek(output_file, flash_file_system->base - flash_os_image->base, SEEK_SET);
-	write_partition(input_file, firmware_offset, fwup_file_system, output_file);
+	write_partition(input_file, info.payload_offset, fwup_file_system, output_file);
 
 	fclose(output_file);
 	fclose(input_file);
-- 
2.39.0




More information about the openwrt-devel mailing list