Skip to content

Implement sparse image support (no deps on libsparse) #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
prefix := /usr/local

QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.c ux.c
QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.c ux.c sparse.c
QDL_OBJS := $(QDL_SRCS:.c=.o)

RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c ux.c
Expand Down
53 changes: 38 additions & 15 deletions firehose.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "sparse.h"
#include "qdl.h"
#include "ufs.h"

Expand Down Expand Up @@ -359,21 +360,24 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
int left;
int ret;
int n;
uint32_t fill_value;

num_sectors = program->num_sectors;

ret = fstat(fd, &sb);
if (ret < 0)
err(1, "failed to stat \"%s\"\n", program->filename);

num_sectors = (sb.st_size + program->sector_size - 1) / program->sector_size;
if (!program->sparse) {
num_sectors = (sb.st_size + program->sector_size - 1) / program->sector_size;

if (program->num_sectors && num_sectors > program->num_sectors) {
ux_err("%s to big for %s truncated to %d\n",
program->filename,
program->label,
program->num_sectors * program->sector_size);
num_sectors = program->num_sectors;
if (program->num_sectors && num_sectors > program->num_sectors) {
ux_err("%s to big for %s truncated to %d\n",
program->filename,
program->label,
program->num_sectors * program->sector_size);
num_sectors = program->num_sectors;
}
}

buf = malloc(max_payload_size);
Expand Down Expand Up @@ -411,19 +415,38 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int

t0 = time(NULL);

lseek(fd, (off_t) program->file_offset * program->sector_size, SEEK_SET);
if (!program->sparse)
lseek(fd, (off_t) program->file_offset * program->sector_size, SEEK_SET);
else {
switch (program->sparse_chunk_type) {
case CHUNK_TYPE_RAW:
lseek(fd, (off_t) program->sparse_chunk_data, SEEK_SET);
break;
case CHUNK_TYPE_FILL:
fill_value = (uint32_t) program->sparse_chunk_data;
for (n = 0; n < max_payload_size; n += sizeof(fill_value))
memcpy(buf + n, &fill_value, sizeof(fill_value));
break;
default:
ux_err("[SPARSE] invalid chunk type\n");
goto out;
}
}

left = num_sectors;
while (left > 0) {
chunk_size = MIN(max_payload_size / program->sector_size, left);

n = read(fd, buf, chunk_size * program->sector_size);
if (n < 0) {
ux_err("failed to read %s\n", program->filename);
goto out;
}
if (!program->sparse || program->sparse_chunk_type != CHUNK_TYPE_FILL) {
n = read(fd, buf, chunk_size * program->sector_size);
if (n < 0) {
ux_err("failed to read %s\n", program->filename);
goto out;
}

if (n < max_payload_size)
memset(buf + n, 0, max_payload_size - n);
if (n < max_payload_size)
memset(buf + n, 0, max_payload_size - n);
}

n = qdl_write(qdl, buf, chunk_size * program->sector_size);
if (n < 0) {
Expand Down
108 changes: 107 additions & 1 deletion program.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

#include "program.h"
#include "qdl.h"
#include "sparse.h"

static struct program *programes;
static struct program *programes_last;
Expand Down Expand Up @@ -80,6 +81,95 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
return 0;
}

static struct program *program_load_sparse(struct program *program, int fd)
{
struct program *program_sparse = NULL;
struct program *programes_sparse = NULL;
struct program *programes_sparse_last = NULL;
char tmp[PATH_MAX];

sparse_header_t sparse_header;
unsigned start_sector, chunk_size, chunk_type, chunk_data;

if (sparse_header_parse(fd, &sparse_header)) {
/*
* If the XML tag "program" contains the attribute 'sparse="true"'
* for a partition node but lacks a sparse header,
* it will be validated against the defined partition size.
* If the sizes match, it is likely that the 'sparse="true"' attribute
* was set by mistake.
*/
if (program->sector_size * program->num_sectors == lseek(fd, 0, SEEK_END)) {
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->sparse = false;
program_sparse->next = NULL;
return program_sparse;
}

ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
program->filename);
return NULL;
}

for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
chunk_type = sparse_chunk_header_parse(fd, &sparse_header, &chunk_size, &chunk_data);

switch (chunk_type) {
case CHUNK_TYPE_RAW:
case CHUNK_TYPE_FILL:
case CHUNK_TYPE_DONT_CARE:
break;
default:
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
i, program->filename);
return NULL;
}

if (chunk_size == 0)
continue;

if (chunk_size % program->sector_size != 0) {
ux_err("[SPARSE] File chunk #%u size %u is not a sector-multiple\n",
i, chunk_size);
return NULL;
}

switch (chunk_type) {
case CHUNK_TYPE_RAW:
case CHUNK_TYPE_FILL:

program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));

program_sparse->next = NULL;
program_sparse->num_sectors = chunk_size / program->sector_size;

program_sparse->sparse_chunk_type = chunk_type;
program_sparse->sparse_chunk_data = chunk_data;

if (programes_sparse) {
programes_sparse_last->next = program_sparse;
programes_sparse_last = program_sparse;
} else {
programes_sparse = program_sparse;
programes_sparse_last = program_sparse;
}

break;
default:
break;
}

start_sector = (unsigned int) strtoul(program->start_sector, NULL, 0);
start_sector += chunk_size / program->sector_size;
sprintf(tmp, "%u", start_sector);
program->start_sector = strdup(tmp);
}

return programes_sparse;
}

static int load_program_tag(xmlNode *node, bool is_nand)
{
struct program *program;
Expand All @@ -94,6 +184,7 @@ static int load_program_tag(xmlNode *node, bool is_nand)
program->label = attr_as_string(node, "label", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->sparse = attr_as_bool(node, "sparse", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);

if (is_nand) {
Expand Down Expand Up @@ -163,6 +254,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
const char *incdir, bool allow_missing)
{
struct program *program;
struct program *program_sparse;
const char *filename;
char tmp[PATH_MAX];
int ret;
Expand Down Expand Up @@ -191,7 +283,21 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
continue;
}

ret = apply(qdl, program, fd);
if (!program->sparse) {
ret = apply(qdl, program, fd);
} else {
program_sparse = program_load_sparse(program, fd);
if (!program_sparse) {
ux_err("[PROGRAM] load sparse failed\n");
return -EINVAL;
}

for (; program_sparse; program_sparse = program_sparse->next) {
ret = apply(qdl, program_sparse, fd);
if (ret)
break;
}
}

close(fd);
if (ret)
Expand Down
4 changes: 4 additions & 0 deletions program.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ struct program {
const char *label;
unsigned num_sectors;
unsigned partition;
bool sparse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool sparse;
bool is_sparse;

nitpick: wonder if it makes sense to put these with is_nand & is_erase ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, "sparse" naming reflects a real XML-attribute "sparse", same as "label", "num_sectors" and etc

const char *start_sector;
unsigned last_sector;

bool is_nand;
bool is_erase;

unsigned sparse_chunk_type;
unsigned sparse_chunk_data;

struct program *next;
};

Expand Down
1 change: 1 addition & 0 deletions qdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
bool attr_as_bool(xmlNode *node, const char *attr, int *errors);

void ux_init(void);
void ux_err(const char *fmt, ...);
Expand Down
Loading