Files
qdl/zipper.c
T
Bjorn Andersson cc6d9c3596 zipper: Introduce support for creating a flashmap zip file
The single-file format of the flashmap zip lends itself very well for
distributing builds, but the tools for generating one isn't broadly
available.

Implement a new "create-zip" operation in QDL, which parses a build from
a contents.xml, generates a flashmap.json and packages all the files
into a flashmap zip file.

The internal structure is flattened and file names are adjusted to avoid
conflicts.

Sparse files are written to the file by storing the relevant chunks in
their decoded form, rather than storing the sparse file (or full
unsparsed file). From an implementation point of view this is easier, as
we don't keep the sparsed program entry around, but it has the two
benefits of us not storing DONT_CARE entries, and it avoids the need for
seeking as we flash the zip file.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-05-14 16:39:30 -05:00

1069 lines
26 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <limits.h>
#include <libgen.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zip.h>
#include "flashmap.h"
#include "firehose.h"
#include "list.h"
#include "qdl.h"
#include "sparse.h"
static char *zipper_basename(const char *path)
{
char *tmp;
char *base;
if (!path || !path[0])
return NULL;
tmp = strdup(path);
if (!tmp)
return NULL;
base = basename(tmp);
base = base ? strdup(base) : NULL;
free(tmp);
return base;
}
static void xml_setpropf(xmlNode *node, const char *attr, const char *fmt, ...)
{
char *buf;
va_list ap;
if (!node || !attr || !fmt)
return;
va_start(ap, fmt);
if (vasprintf(&buf, fmt, ap) < 0) {
va_end(ap);
return;
}
va_end(ap);
xmlSetProp(node, (xmlChar *)attr, (xmlChar *)buf);
free(buf);
}
struct zipper_name_entry {
char *name;
struct list_head node;
};
struct zipper_file_rename {
enum qdl_storage_type storage_type;
char *source_name;
char *zip_name;
struct list_head node;
};
struct zipper_storage_ctx {
enum qdl_storage_type storage_type;
const char *memory;
xmlDoc *program_doc;
xmlNode *program_root;
xmlDoc *patch_doc;
xmlNode *patch_root;
char *program_xml_name;
char *patch_xml_name;
struct list_head node;
};
struct flashmap_json_buf {
char *buf;
size_t len;
size_t cap;
bool error;
};
static void zipper_free_name_entries(struct list_head *names)
{
struct zipper_name_entry *entry;
struct zipper_name_entry *next;
list_for_each_entry_safe(entry, next, names, node) {
list_del(&entry->node);
free(entry->name);
free(entry);
}
}
static bool zipper_name_is_used(struct list_head *names, const char *name)
{
struct zipper_name_entry *entry;
list_for_each_entry(entry, names, node) {
if (!strcmp(entry->name, name))
return true;
}
return false;
}
static int zipper_add_name_entry(struct list_head *names, const char *name)
{
struct zipper_name_entry *entry;
entry = calloc(1, sizeof(*entry));
if (!entry)
return -1;
entry->name = strdup(name);
if (!entry->name) {
free(entry);
return -1;
}
list_append(names, &entry->node);
return 0;
}
static int zipper_add_buffer(zip_t *zip, const char *name, const void *data, size_t len)
{
zip_source_t *source;
zip_int64_t idx;
void *copy = NULL;
if (len) {
copy = malloc(len);
if (!copy) {
ux_err("failed to allocate zip member \"%s\"\n", name);
return -1;
}
memcpy(copy, data, len);
}
source = zip_source_buffer(zip, copy, len, 1);
if (!source) {
ux_err("failed to create zip source for \"%s\"\n", name);
free(copy);
return -1;
}
idx = zip_file_add(zip, name, source, ZIP_FL_OVERWRITE);
if (idx < 0) {
ux_err("failed to add \"%s\" to zip\n", name);
zip_source_free(source);
return -1;
}
return 0;
}
static int zipper_add_file(zip_t *zip, const char *name, const char *path)
{
zip_source_t *source;
zip_int64_t idx;
source = zip_source_file(zip, path, 0, -1);
if (!source) {
ux_err("failed to create zip file source for \"%s\"\n", path);
return -1;
}
idx = zip_file_add(zip, name, source, ZIP_FL_OVERWRITE);
if (idx < 0) {
ux_err("failed to add \"%s\" to zip\n", name);
zip_source_free(source);
return -1;
}
return 0;
}
static void zipper_close_progress_cb(zip_t *archive __unused,
double progress,
void *userdata)
{
unsigned int value;
const char *label = userdata ? (const char *)userdata : "finalize";
if (progress < 0.0)
progress = 0.0;
else if (progress > 1.0)
progress = 1.0;
value = (unsigned int)(progress * 100000.0);
ux_progress(label, value, 100000);
}
static struct zipper_file_rename *zipper_find_file_rename(struct list_head *renames,
enum qdl_storage_type storage_type,
const char *source_name)
{
struct zipper_file_rename *rename;
list_for_each_entry(rename, renames, node) {
if (rename->storage_type == storage_type &&
!strcmp(rename->source_name, source_name))
return rename;
}
return NULL;
}
static void zipper_free_file_renames(struct list_head *renames)
{
struct zipper_file_rename *rename;
struct zipper_file_rename *next;
list_for_each_entry_safe(rename, next, renames, node) {
list_del(&rename->node);
free(rename->source_name);
free(rename->zip_name);
free(rename);
}
}
static struct zipper_storage_ctx *zipper_find_storage_ctx(struct list_head *storages,
enum qdl_storage_type storage_type)
{
struct zipper_storage_ctx *storage;
list_for_each_entry(storage, storages, node) {
if (storage->storage_type == storage_type)
return storage;
}
return NULL;
}
static void zipper_free_storage_contexts(struct list_head *storages)
{
struct zipper_storage_ctx *storage;
struct zipper_storage_ctx *next;
list_for_each_entry_safe(storage, next, storages, node) {
list_del(&storage->node);
xmlFreeDoc(storage->program_doc);
xmlFreeDoc(storage->patch_doc);
free(storage->program_xml_name);
free(storage->patch_xml_name);
free(storage);
}
}
static struct zipper_storage_ctx *zipper_create_storage_ctx(enum qdl_storage_type storage_type)
{
struct zipper_storage_ctx *storage;
const char *memory;
memory = encode_storage_type(storage_type);
if (!memory) {
ux_err("unknown storage type %d\n", storage_type);
return NULL;
}
storage = calloc(1, sizeof(*storage));
if (!storage)
return NULL;
storage->storage_type = storage_type;
storage->memory = memory;
storage->program_doc = xmlNewDoc((xmlChar *)"1.0");
storage->program_root = xmlNewNode(NULL, (xmlChar *)"data");
if (!storage->program_doc || !storage->program_root)
goto err;
xmlDocSetRootElement(storage->program_doc, storage->program_root);
storage->patch_doc = xmlNewDoc((xmlChar *)"1.0");
storage->patch_root = xmlNewNode(NULL, (xmlChar *)"patches");
if (!storage->patch_doc || !storage->patch_root)
goto err;
xmlDocSetRootElement(storage->patch_doc, storage->patch_root);
return storage;
err:
xmlFreeDoc(storage->program_doc);
xmlFreeDoc(storage->patch_doc);
free(storage);
return NULL;
}
static int zipper_storage_append_program(struct zipper_storage_ctx *storage, struct firehose_op *op,
const char *filename, unsigned int file_offset)
{
xmlNode *node;
if (!filename)
return 0;
node = xmlNewChild(storage->program_root, NULL, (xmlChar *)"program", NULL);
if (!node)
return -1;
xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%u", op->sector_size);
xml_setpropf(node, "filename", "%s", filename);
xml_setpropf(node, "num_partition_sectors", "%u", op->num_sectors);
xml_setpropf(node, "physical_partition_number", "%d", op->partition);
xml_setpropf(node, "start_sector", "%s", op->start_sector ? op->start_sector : "0");
if (op->label)
xml_setpropf(node, "label", "%s", op->label);
if (op->is_nand) {
xml_setpropf(node, "PAGES_PER_BLOCK", "%u", op->pages_per_block);
xml_setpropf(node, "last_sector", "%u", op->last_sector);
} else {
xml_setpropf(node, "file_sector_offset", "%u", file_offset);
}
return 0;
}
static int zipper_register_raw_program_file(zip_t *zip, struct list_head *name_entries,
const char *memory, struct firehose_op *op,
unsigned int *raw_idx, char **zip_name)
{
zip_source_t *source;
zip_int64_t idx;
uint64_t raw_size;
char *filename;
int ret;
if (!op->sector_size || !op->num_sectors || op->sparse_offset < 0) {
ux_err("invalid sparse raw chunk parameters\n");
return -1;
}
if ((uint64_t)op->num_sectors > INT64_MAX / op->sector_size) {
ux_err("sparse raw chunk too large\n");
return -1;
}
raw_size = (uint64_t)op->num_sectors * op->sector_size;
filename = calloc(1, strlen(memory) + 31);
if (!filename)
return -1;
snprintf(filename, strlen(memory) + 31, "%s_raw_%06u.bin", memory, (*raw_idx)++);
if (zipper_name_is_used(name_entries, filename)) {
ux_err("duplicate generated raw filename \"%s\"\n", filename);
goto err_free_filename;
}
source = zip_source_file(zip, op->filename, (zip_uint64_t)op->sparse_offset,
(zip_int64_t)raw_size);
if (!source) {
ux_err("failed to create zip file source for \"%s\"\n", op->filename);
goto err_free_filename;
}
idx = zip_file_add(zip, filename, source, ZIP_FL_OVERWRITE);
if (idx < 0) {
ux_err("failed to add \"%s\" to zip\n", filename);
goto err_free_zip_source;
}
ret = zipper_add_name_entry(name_entries, filename);
if (ret) {
/* Don't free "source", it's owned by libzip since zip_file_add() */
goto err_free_filename;
}
*zip_name = filename;
return 0;
err_free_zip_source:
zip_source_free(source);
err_free_filename:
free(filename);
return -1;
}
static int zipper_register_fill_program_file(zip_t *zip, struct list_head *name_entries,
const char *memory, struct firehose_op *op,
unsigned int *fill_idx, char **zip_name)
{
uint32_t fill_value = op->sparse_fill_value;
size_t fill_size;
char *filename;
void *buf;
size_t i;
int ret;
if (!op->sector_size || !op->num_sectors) {
ux_err("invalid sparse fill chunk parameters\n");
return -1;
}
if ((size_t)op->num_sectors > SIZE_MAX / op->sector_size) {
ux_err("sparse fill chunk too large\n");
return -1;
}
fill_size = (size_t)op->num_sectors * op->sector_size;
filename = calloc(1, strlen(memory) + 32);
if (!filename)
return -1;
snprintf(filename, strlen(memory) + 32, "%s_fill_%06u.bin", memory, (*fill_idx)++);
if (zipper_name_is_used(name_entries, filename)) {
ux_err("duplicate generated fill filename \"%s\"\n", filename);
free(filename);
return -1;
}
buf = malloc(fill_size);
if (!buf) {
free(filename);
return -1;
}
for (i = 0; i + sizeof(fill_value) <= fill_size; i += sizeof(fill_value))
memcpy((char *)buf + i, &fill_value, sizeof(fill_value));
while (i < fill_size) {
((char *)buf)[i] = ((char *)&fill_value)[i % sizeof(fill_value)];
i++;
}
ret = zipper_add_buffer(zip, filename, buf, fill_size);
free(buf);
if (ret) {
free(filename);
return -1;
}
ret = zipper_add_name_entry(name_entries, filename);
if (ret) {
free(filename);
return -1;
}
*zip_name = filename;
return 0;
}
static int zipper_register_program_file(zip_t *zip, struct list_head *name_entries,
struct list_head *renames,
enum qdl_storage_type storage_type, const char *memory,
const char *source_name, const char **zip_name)
{
struct zipper_file_rename *rename;
char *file_basename;
char *renamed_file;
int ret;
rename = zipper_find_file_rename(renames, storage_type, source_name);
if (rename) {
*zip_name = rename->zip_name;
return 0;
}
file_basename = zipper_basename(source_name);
if (!file_basename) {
ux_err("failed to parse basename for \"%s\"\n", source_name);
return -1;
}
if (asprintf(&renamed_file, "%s_%s", memory, file_basename) < 0) {
free(file_basename);
return -1;
}
free(file_basename);
if (zipper_name_is_used(name_entries, renamed_file)) {
ux_err("duplicate zip filename \"%s\"\n", renamed_file);
free(renamed_file);
return -1;
}
ret = zipper_add_file(zip, renamed_file, source_name);
if (ret) {
ux_err("failed to copy \"%s\"\n", source_name);
free(renamed_file);
return -1;
}
ret = zipper_add_name_entry(name_entries, renamed_file);
if (ret) {
free(renamed_file);
return -1;
}
rename = calloc(1, sizeof(*rename));
if (!rename) {
free(renamed_file);
return -1;
}
rename->storage_type = storage_type;
rename->source_name = strdup(source_name);
rename->zip_name = renamed_file;
if (!rename->source_name) {
free(rename->zip_name);
free(rename);
return -1;
}
list_append(renames, &rename->node);
*zip_name = rename->zip_name;
return 0;
}
static int zipper_storage_append_erase(struct zipper_storage_ctx *storage, struct firehose_op *op)
{
xmlNode *node;
node = xmlNewChild(storage->program_root, NULL, (xmlChar *)"erase", NULL);
if (!node)
return -1;
xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%u", op->sector_size);
xml_setpropf(node, "num_partition_sectors", "%u", op->num_sectors);
xml_setpropf(node, "physical_partition_number", "%d", op->partition);
xml_setpropf(node, "start_sector", "%s", op->start_sector ? op->start_sector : "0");
if (op->is_nand)
xml_setpropf(node, "PAGES_PER_BLOCK", "%u", op->pages_per_block);
return 0;
}
static int zipper_storage_append_patch(struct zipper_storage_ctx *storage, struct firehose_op *op)
{
xmlNode *node;
node = xmlNewChild(storage->patch_root, NULL, (xmlChar *)"patch", NULL);
if (!node)
return -1;
xml_setpropf(node, "SECTOR_SIZE_IN_BYTES", "%u", op->sector_size);
xml_setpropf(node, "byte_offset", "%u", op->byte_offset);
xml_setpropf(node, "filename", "%s", op->filename ? op->filename : "");
xml_setpropf(node, "physical_partition_number", "%d", op->partition);
xml_setpropf(node, "size_in_bytes", "%u", op->size_in_bytes);
xml_setpropf(node, "start_sector", "%s", op->start_sector ? op->start_sector : "0");
xml_setpropf(node, "value", "%s", op->value ? op->value : "");
xml_setpropf(node, "what", "%s", op->what ? op->what : "");
return 0;
}
static void flashmap_json_grow(struct flashmap_json_buf *json, size_t extra)
{
size_t needed;
size_t next_cap;
char *new_buf;
if (json->error)
return;
needed = json->len + extra + 1;
if (needed <= json->cap)
return;
next_cap = json->cap ? json->cap : 256;
while (next_cap < needed) {
if (next_cap > SIZE_MAX / 2) {
json->error = true;
return;
}
next_cap *= 2;
}
new_buf = realloc(json->buf, next_cap);
if (!new_buf) {
json->error = true;
return;
}
json->buf = new_buf;
json->cap = next_cap;
}
static void flashmap_json_append_raw(struct flashmap_json_buf *json, const char *s, size_t len)
{
if (json->error)
return;
flashmap_json_grow(json, len);
if (json->error)
return;
memcpy(json->buf + json->len, s, len);
json->len += len;
json->buf[json->len] = '\0';
}
static void flashmap_json_appendf(struct flashmap_json_buf *json, const char *fmt, ...)
{
va_list ap;
va_list aq;
int needed;
if (json->error)
return;
va_start(ap, fmt);
va_copy(aq, ap);
needed = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);
if (needed < 0) {
va_end(ap);
json->error = true;
return;
}
flashmap_json_grow(json, needed);
if (json->error) {
va_end(ap);
return;
}
vsnprintf(json->buf + json->len, json->cap - json->len, fmt, ap);
json->len += needed;
va_end(ap);
}
static void flashmap_json_append_escaped_string(struct flashmap_json_buf *json, const char *s)
{
unsigned char ch;
char esc[7];
flashmap_json_append_raw(json, "\"", 1);
for (; !json->error && *s; s++) {
ch = (unsigned char)*s;
switch (ch) {
case '"':
flashmap_json_append_raw(json, "\\\"", 2);
break;
case '\\':
flashmap_json_append_raw(json, "\\\\", 2);
break;
case '\b':
flashmap_json_append_raw(json, "\\b", 2);
break;
case '\f':
flashmap_json_append_raw(json, "\\f", 2);
break;
case '\n':
flashmap_json_append_raw(json, "\\n", 2);
break;
case '\r':
flashmap_json_append_raw(json, "\\r", 2);
break;
case '\t':
flashmap_json_append_raw(json, "\\t", 2);
break;
default:
if (ch < 0x20) {
snprintf(esc, sizeof(esc), "\\u%04x", ch);
flashmap_json_append_raw(json, esc, 6);
} else {
flashmap_json_append_raw(json, (const char *)&ch, 1);
}
break;
}
}
flashmap_json_append_raw(json, "\"", 1);
}
static int zipper_add_programmer_images(zip_t *zip, struct list_head *name_entries,
char **programmer_entries,
struct sahara_image *images)
{
char *basename_name;
char *zip_name;
int count = 0;
int i;
int ret;
for (i = 0; i < MAPPING_SZ; i++) {
if (!images[i].ptr)
continue;
basename_name = zipper_basename(images[i].name ? images[i].name : "programmer.bin");
if (!basename_name)
return -1;
if (zipper_name_is_used(name_entries, basename_name)) {
zip_name = calloc(1, strlen(basename_name) + 20);
if (!zip_name) {
free(basename_name);
return -1;
}
snprintf(zip_name, strlen(basename_name) + 20, "sahara%u_%s", i, basename_name);
free(basename_name);
} else {
zip_name = basename_name;
}
if (zipper_name_is_used(name_entries, zip_name)) {
ux_err("duplicate zip filename \"%s\"\n", zip_name);
free(zip_name);
return -1;
}
ret = zipper_add_buffer(zip, zip_name, images[i].ptr, images[i].len);
if (ret) {
free(zip_name);
return ret;
}
ret = zipper_add_name_entry(name_entries, zip_name);
if (ret) {
free(zip_name);
return ret;
}
programmer_entries[i] = zip_name;
count++;
}
return count ? 0 : -1;
}
static void zipper_free_programmer_entries(char **programmer_entries)
{
int i;
for (i = 0; i < MAPPING_SZ; i++) {
free(programmer_entries[i]);
programmer_entries[i] = NULL;
}
}
static int zipper_add_storage_xml(zip_t *zip, struct list_head *name_entries,
struct zipper_storage_ctx *storage)
{
xmlChar *xml_data;
int len;
int ret;
storage->program_xml_name = calloc(1, strlen(storage->memory) + strlen("_rawprogram.xml") + 1);
if (!storage->program_xml_name)
return -1;
snprintf(storage->program_xml_name, strlen(storage->memory) + strlen("_rawprogram.xml") + 1,
"%s_rawprogram.xml", storage->memory);
storage->patch_xml_name = calloc(1, strlen(storage->memory) + strlen("_patch.xml") + 1);
if (!storage->patch_xml_name)
return -1;
snprintf(storage->patch_xml_name, strlen(storage->memory) + strlen("_patch.xml") + 1,
"%s_patch.xml", storage->memory);
if (zipper_name_is_used(name_entries, storage->program_xml_name) ||
zipper_name_is_used(name_entries, storage->patch_xml_name)) {
ux_err("duplicate xml filename for memory \"%s\"\n", storage->memory);
return -1;
}
xmlDocDumpMemory(storage->program_doc, &xml_data, &len);
if (!xml_data || len < 0)
return -1;
ret = zipper_add_buffer(zip, storage->program_xml_name, xml_data, (size_t)len);
xmlFree(xml_data);
if (ret)
return ret;
ret = zipper_add_name_entry(name_entries, storage->program_xml_name);
if (ret)
return ret;
xmlDocDumpMemory(storage->patch_doc, &xml_data, &len);
if (!xml_data || len < 0)
return -1;
ret = zipper_add_buffer(zip, storage->patch_xml_name, xml_data, (size_t)len);
xmlFree(xml_data);
if (ret)
return ret;
ret = zipper_add_name_entry(name_entries, storage->patch_xml_name);
if (ret)
return ret;
return 0;
}
static int flashmap_build_json(struct flashmap_json_buf *json,
char **programmer_entries,
struct list_head *storages)
{
struct zipper_storage_ctx *storage;
bool first;
int i;
flashmap_json_appendf(json, "{\n");
flashmap_json_appendf(json, " \"version\": \"1.2.0-qdl\",\n");
flashmap_json_appendf(json, " \"products\": [\n");
flashmap_json_appendf(json, " {\n");
flashmap_json_appendf(json, " \"name\": \"contents\",\n");
flashmap_json_appendf(json, " \"layouts\": [\n");
flashmap_json_appendf(json, " {\n");
flashmap_json_appendf(json, " \"name\": \"layout0\",\n");
flashmap_json_appendf(json, " \"programmer\": {\n");
first = true;
for (i = 0; i < MAPPING_SZ; i++) {
if (!programmer_entries[i])
continue;
if (!first)
flashmap_json_appendf(json, ",\n");
first = false;
flashmap_json_appendf(json, " \"%u\": ", i);
flashmap_json_append_escaped_string(json, programmer_entries[i]);
}
flashmap_json_appendf(json, "\n");
flashmap_json_appendf(json, " },\n");
flashmap_json_appendf(json, " \"programmable\": [\n");
first = true;
list_for_each_entry(storage, storages, node) {
if (!first)
flashmap_json_appendf(json, ",\n");
first = false;
flashmap_json_appendf(json, " {\n");
flashmap_json_appendf(json, " \"memory\": \"%s\",\n", storage->memory);
flashmap_json_appendf(json, " \"slot\": 0,\n");
flashmap_json_appendf(json, " \"files\": [\n");
flashmap_json_appendf(json, " ");
flashmap_json_append_escaped_string(json, storage->program_xml_name);
flashmap_json_appendf(json, ",\n");
flashmap_json_appendf(json, " ");
flashmap_json_append_escaped_string(json, storage->patch_xml_name);
flashmap_json_appendf(json, "\n");
flashmap_json_appendf(json, " ]\n");
flashmap_json_appendf(json, " }");
}
flashmap_json_appendf(json, "\n");
flashmap_json_appendf(json, " ]\n");
flashmap_json_appendf(json, " }\n");
flashmap_json_appendf(json, " ]\n");
flashmap_json_appendf(json, " }\n");
flashmap_json_appendf(json, " ]\n");
flashmap_json_appendf(json, "}\n");
return json->error ? -1 : 0;
}
int zipper_write(const char *filename, struct list_head *ops, struct sahara_image *images)
{
char *programmer_entries[MAPPING_SZ] = {0};
struct list_head file_renames = LIST_INIT(file_renames);
struct zipper_storage_ctx *storage = NULL;
struct list_head storages = LIST_INIT(storages);
struct list_head name_entries = LIST_INIT(name_entries);
struct flashmap_json_buf json = {0};
enum qdl_storage_type current_storage = QDL_STORAGE_UNKNOWN;
unsigned int raw_idx = 0;
unsigned int fill_idx = 0;
struct firehose_op *op;
unsigned int file_offset;
const char *zip_name;
char *sparse_zip_name;
char *tmp_filename = NULL;
zip_t *zip;
int tmp_fd;
unsigned int programmer_count = 0;
unsigned int program_count = 0;
unsigned int patch_count = 0;
unsigned int erase_count = 0;
int zip_error = 0;
int ret = -1;
tmp_filename = malloc(strlen(filename) + 8);
if (!tmp_filename)
goto out_cleanup;
sprintf(tmp_filename, "%s.XXXXXX", filename);
tmp_fd = mkstemp(tmp_filename);
if (tmp_fd < 0) {
ux_err("failed to create temporary output zip \"%s\": %s\n",
tmp_filename, strerror(errno));
goto out_cleanup;
}
close(tmp_fd);
zip = zip_open(tmp_filename, ZIP_CREATE | ZIP_TRUNCATE, &zip_error);
if (!zip) {
ux_err("failed to open output zip \"%s\"\n", tmp_filename);
ret = -1;
goto out_cleanup;
}
list_for_each_entry(op, ops, node) {
if (op->type == FIREHOSE_OP_CONFIGURE) {
current_storage = op->storage_type;
storage = zipper_find_storage_ctx(&storages, current_storage);
if (!storage) {
storage = zipper_create_storage_ctx(current_storage);
if (!storage)
goto out_discard_zip;
list_append(&storages, &storage->node);
}
continue;
}
if (!storage) {
ux_err("internal error: missing configure operation\n");
goto out_discard_zip;
}
switch (op->type) {
case FIREHOSE_OP_PROGRAM:
if (!op->filename)
break;
if (op->zip) {
ux_err("create-zip does not support program entries sourced from zip archives\n");
goto out_discard_zip;
}
sparse_zip_name = NULL;
zip_name = NULL;
if (op->sparse) {
switch (op->sparse_chunk_type) {
case CHUNK_TYPE_RAW:
ret = zipper_register_raw_program_file(zip, &name_entries, storage->memory, op,
&raw_idx, &sparse_zip_name);
break;
case CHUNK_TYPE_FILL:
ret = zipper_register_fill_program_file(zip, &name_entries, storage->memory, op,
&fill_idx, &sparse_zip_name);
break;
default:
ux_err("unsupported sparse chunk type %u in \"%s\"\n",
op->sparse_chunk_type, op->filename);
goto out_discard_zip;
}
if (ret)
goto out_discard_zip;
zip_name = sparse_zip_name;
file_offset = 0;
} else {
ret = zipper_register_program_file(zip, &name_entries, &file_renames,
current_storage, storage->memory,
op->filename, &zip_name);
if (ret)
goto out_discard_zip;
file_offset = op->file_offset;
}
ret = zipper_storage_append_program(storage, op, zip_name, file_offset);
free(sparse_zip_name);
if (ret)
goto out_discard_zip;
program_count++;
break;
case FIREHOSE_OP_ERASE:
ret = zipper_storage_append_erase(storage, op);
if (ret)
goto out_discard_zip;
erase_count++;
break;
case FIREHOSE_OP_PATCH:
/*
* Only filename=="DISK" entries are actually flashed,
* so omit the others from the zip file as well.
*/
if (!op->filename || strcmp(op->filename, "DISK"))
break;
ret = zipper_storage_append_patch(storage, op);
if (ret)
goto out_discard_zip;
patch_count++;
break;
default:
break;
}
}
if (list_empty(&storages)) {
ux_err("no configurable storage operations to write\n");
goto out_discard_zip;
}
ret = zipper_add_programmer_images(zip, &name_entries, programmer_entries, images);
if (ret) {
ux_err("no programmer images available to write\n");
goto out_discard_zip;
}
for (int i = 0; i < MAPPING_SZ; i++) {
if (programmer_entries[i])
programmer_count++;
}
list_for_each_entry(storage, &storages, node) {
ret = zipper_add_storage_xml(zip, &name_entries, storage);
if (ret)
goto out_discard_zip;
}
ret = flashmap_build_json(&json, programmer_entries, &storages);
if (ret)
goto out_discard_zip;
ret = zipper_add_buffer(zip, "flashmap.json", json.buf, json.len);
if (ret)
goto out_discard_zip;
ux_info("found %u programmers, %u program entries, %u patch entries, %u erase entries\n",
programmer_count, program_count, patch_count, erase_count);
ux_info("finalizing archive, this can take a while for large images\n");
if (zip_register_progress_callback_with_state(zip, 0.001,
zipper_close_progress_cb,
NULL, (void *)filename) < 0)
ux_debug("unable to register zip close progress callback\n");
if (zip_close(zip) < 0) {
ux_err("failed to finalize output zip\n");
zip_discard(zip);
ret = -1;
goto out_cleanup;
}
if (rename(tmp_filename, filename) < 0) {
ux_err("failed to rename temporary output zip \"%s\" to \"%s\": %s\n",
tmp_filename, filename, strerror(errno));
ret = -1;
goto out_cleanup;
}
ux_info("successfully created %s\n", filename);
ret = 0;
goto out_cleanup;
out_discard_zip:
zip_discard(zip);
out_cleanup:
if (ret && tmp_filename)
unlink(tmp_filename);
free(tmp_filename);
free(json.buf);
zipper_free_name_entries(&name_entries);
zipper_free_file_renames(&file_renames);
zipper_free_programmer_entries(programmer_entries);
zipper_free_storage_contexts(&storages);
return ret;
}