fip_create: add support for image unpacking

This patch adds support for image unpacking to the FIP packaging
tool. Command line option '-u,--unpack' may be used to unpack the
contents of an existing FIP file into the working directory. The
tool uses default hardcoded filenames for the unpacked images. If
the files already exist, they can be overwritten by specifying the
option '-f,--force'.

Change-Id: I360b11d9c5403e8c0a7a9cac32c1d90ebb228063
diff --git a/tools/fip_create/fip_create.c b/tools/fip_create/fip_create.c
index dab9eb6..7bce348 100644
--- a/tools/fip_create/fip_create.c
+++ b/tools/fip_create/fip_create.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -42,13 +42,17 @@
 #define OPT_TOC_ENTRY 0
 #define OPT_DUMP 'd'
 #define OPT_HELP 'h'
-#define OPT_STR "dh"
+#define OPT_UNPACK 'u'
+#define OPT_FORCE 'f'
+#define OPT_STR "dfhu"
 
 static file_info_t files[MAX_FILES];
 static unsigned file_info_count;
 static uuid_t uuid_null = {0};
 static int do_dump;
 static int do_pack;
+static int do_unpack;
+static int do_force;
 
 /*
  * TODO: Add ability to specify and flag different file types.
@@ -127,6 +131,8 @@
 	printf("Options:\n");
 	printf("\t-h,--help: Print this help message and exit\n");
 	printf("\t-d,--dump: Print contents of FIP after update\n");
+	printf("\t-u,--unpack: Unpack images from an existing FIP\n");
+	printf("\t-f,--force: Overwrite existing files when unpacking images\n\n");
 	printf("Components that can be added/updated:\n");
 	for (; entry->command_line_name != NULL; entry++) {
 		printf("\t--%s%s\t\t%s",
@@ -376,6 +382,109 @@
 }
 
 
+/*
+ * Unpack all images from an existing FIP
+ *
+ * Images will be unpacked into the working directory using filenames as
+ * specified by the corresponding command line option plus the 'bin' extension.
+ * For example, the image specified by the --soc-fw option will be unpacked as
+ * 'soc-fw.bin'
+ */
+static int unpack_images(void)
+{
+	FILE *stream;
+	size_t bytes_written;
+	file_info_t *file_info;
+	char *filename[MAX_FILES];
+	int status, ret = 0;
+	unsigned int i, idx, num_img;
+	struct stat st;
+	size_t len;
+
+	/* Make the output filenames */
+	for (idx = 0; idx < file_info_count; idx++) {
+		filename[idx] = NULL;
+		file_info = &files[idx];
+		if (file_info->image_buffer == NULL) {
+			continue;
+		}
+		len = strlen(file_info->entry->command_line_name);
+		filename[idx] = malloc(len + 5); /* ".bin" + '\0' */
+		if (filename[idx] == NULL) {
+			printf("ERROR: out of memory\n");
+			for (i = 0; i < idx; i++) {
+				free(filename[i]);
+			}
+			return ENOMEM;
+		}
+		strcpy(filename[idx], file_info->entry->command_line_name);
+		strcat(filename[idx], ".bin");
+	}
+
+
+	/* Check if output files already exist in the filesystem. We perform
+	 * this check before any other action, so if any of the files
+	 * exists, nothing is unpacked. If force overwrite is enabled, we skip
+	 * this check */
+	if (!do_force) {
+		for (idx = 0; idx < file_info_count; idx++) {
+			file_info = &files[idx];
+			if (file_info->image_buffer == NULL) {
+				continue;
+			}
+			status = stat(filename[idx], &st);
+			if (!status) {
+				printf("File '%s' exists. Use --force to overwrite.\n",
+				       filename[idx]);
+				printf("Process aborted.\n");
+				ret = EEXIST;
+				goto unpack_images_free;
+			}
+		}
+	}
+
+	printf("Unpacking images...\n");
+
+	/* Write the images to files */
+	num_img = 0;
+	for (idx = 0; idx < file_info_count; idx++) {
+		file_info = &files[idx];
+		if (file_info->image_buffer == NULL) {
+			continue;
+		}
+		/* Unpack the image to a file */
+		stream = fopen(filename[idx], "w");
+		if (!stream) {
+			printf("ERROR: cannot open '%s' for writing\n",
+			       filename[idx]);
+			ret = EIO;
+			goto unpack_images_free;
+		}
+		bytes_written = fwrite(file_info->image_buffer, sizeof(uint8_t),
+				       file_info->size, stream);
+		fclose(stream);
+
+		if (bytes_written != file_info->size) {
+			printf("ERROR: Incorrect write for file \"%s\": Size=%u,"
+			       "Written=%lu bytes.\n", filename[idx], file_info->size,
+			       bytes_written);
+			ret = EIO;
+			goto unpack_images_free;
+		}
+		num_img++;
+	}
+
+	printf("Done. %u images unpacked\n", num_img);
+
+unpack_images_free:
+	for (idx = 0; idx < file_info_count; idx++) {
+		free(filename[idx]);
+	}
+
+	return ret;
+}
+
+
 static void dump_toc(void)
 {
 	unsigned int index = 0;
@@ -595,6 +704,14 @@
 			print_usage();
 			exit(0);
 
+		case OPT_UNPACK:
+			do_unpack = 1;
+			break;
+
+		case OPT_FORCE:
+			do_force = 1;
+			break;
+
 		default:
 			/* Unrecognised options are caught in get_filename() */
 			break;
@@ -610,16 +727,17 @@
 	int i;
 	int status;
 	char *fip_filename;
+	struct stat st;
 
 	/* Clear file list table. */
 	memset(files, 0, sizeof(files));
 
 	/* Initialise for getopt_long().
 	 * Use image table as defined at top of file to get options.
-	 * Add 'dump' option, 'help' option and end marker.
+	 * Add common options and end marker.
 	 */
 	static struct option long_options[(sizeof(toc_entry_lookup_list)/
-					   sizeof(entry_lookup_list_t)) + 2];
+					   sizeof(entry_lookup_list_t)) + 4];
 
 	for (i = 0;
 	     /* -1 because we dont want to process end marker in toc table */
@@ -644,6 +762,18 @@
 	long_options[i].flag = 0;
 	long_options[i].val = OPT_HELP;
 
+	/* Add '--unpack' option */
+	long_options[++i].name = "unpack";
+	long_options[i].has_arg = 0;
+	long_options[i].flag = 0;
+	long_options[i].val = OPT_UNPACK;
+
+	/* Add '--force' option */
+	long_options[++i].name = "force";
+	long_options[i].has_arg = 0;
+	long_options[i].flag = 0;
+	long_options[i].val = OPT_FORCE;
+
 	/* Zero the last entry (required) */
 	long_options[++i].name = 0;
 	long_options[i].has_arg = 0;
@@ -684,10 +814,28 @@
 		return 0;
 	}
 
-	/* Processed all command line options. Create/update the package if
-	 * required.
-	 */
-	if (do_pack) {
+	/* Unpack images from FIP always takes precedence over packaging. In
+	 * the future, there will be different commands for each action and
+	 * only one will be specified in the command line */
+	if (do_unpack) {
+		status = stat(fip_filename, &st);
+		if (status != 0) {
+			printf("ERROR: cannot open %s\n", fip_filename);
+			return status;
+		}
+		/* Warning if user has specified images */
+		if (do_pack) {
+			printf("WARNING: Unpack option specified. Input images "
+			       "will be ignored.\n");
+		}
+		status = unpack_images();
+		if (status != 0) {
+			printf("ERROR: failed to unpack package (status = %d).\n",
+			       status);
+			return status;
+		}
+	} else if (do_pack) {
+		/* Create/update FIP */
 		status = pack_images(fip_filename);
 		if (status != 0) {
 			printf("Failed to create package (status = %d).\n",