Skip to content

Implement sparse image support #30

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
OUT := qdl

CFLAGS := -O2 -Wall -g `pkg-config --cflags libxml-2.0`
LDFLAGS := `pkg-config --libs libxml-2.0 libudev`
CFLAGS := -O2 -Wall -g `pkg-config --cflags libxml-2.0` -I/usr/include/android
LDFLAGS := `pkg-config --libs libxml-2.0 libudev` -L/usr/lib/x86_64-linux-gnu/android -Wl,-rpath=/usr/lib/x86_64-linux-gnu/android -lsparse
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this part of a commonly available package in most distributions?
What can be done about the "x86_64"-part, I'm exclusively running qdl on my ARM64 laptop.

I was under the impression that the sparse format essentially is a form of run-length-encoding to avoid encoding the empty regions in the image. Would it be possible to implement this in qdl, instead of relying on a 3rd party library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@andersson I don't have the bandwidth to make this modification right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lumag I tried to add sparse support to qdl, but lost bandwidth. Are you interested in sparse support ?

prefix := /usr/local

SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
Expand Down
124 changes: 108 additions & 16 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/sparse.h>
#include "qdl.h"
#include "ufs.h"

Expand Down Expand Up @@ -323,6 +324,68 @@ static int firehose_erase(struct qdl_device *qdl, struct program *program)
return ret;
}


struct sparse_cb_info_t {
struct qdl_device *qdl;
struct program *program;
void *payload;
size_t payload_len;
size_t write_count;
size_t total_write_count;
};

static int firehose_program_sparse(void *priv, const void *data, size_t len, unsigned int block, unsigned int nr_blocks)
{
int left;
struct sparse_cb_info_t *priv_info = (struct sparse_cb_info_t *)priv;
size_t chunk_size;
size_t data_write_count = 0;
int n;
char *p_data = (char *)data;
int ret = 0;

// TODO progress report using block / nr_blocks ?

left = (priv_info->payload_len + len) / priv_info->program->sector_size;
while (left > 0) {
chunk_size = MIN(max_payload_size / priv_info->program->sector_size, left);

n = chunk_size * priv_info->program->sector_size - priv_info->payload_len;
memcpy(priv_info->payload + priv_info->payload_len, p_data + data_write_count, n);
data_write_count += n;

priv_info->payload_len += n;
if (priv_info->payload_len < max_payload_size && (priv_info->write_count + priv_info->payload_len) < priv_info->total_write_count) {
if (chunk_size < left)
continue;
else
break;
}

if (priv_info->payload_len < max_payload_size)
memset(priv_info->payload + n, 0, max_payload_size - n);

n = qdl_write(priv_info->qdl, priv_info->payload, chunk_size * priv_info->program->sector_size);
if (n < 0) {
err(1, "failed to write");
ret = -1;
}

if (n != chunk_size * priv_info->program->sector_size) {
err(1, "failed to write full sector");
ret = -1;
}

priv_info->write_count += n;

priv_info->payload_len = 0U;

left -= chunk_size;
}

return ret;
}

static int firehose_program(struct qdl_device *qdl, struct program *program, int fd)
{
unsigned num_sectors;
Expand All @@ -337,14 +400,28 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
int left;
int ret;
int n;
struct sparse_file *s_file = NULL;
struct sparse_cb_info_t sparse_cb_info;

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;
lseek(fd, program->file_offset * program->sector_size, SEEK_SET);

if (program->is_sparse) {
// TODO check last parameter
s_file = sparse_file_import(fd, true, false);

// TODO check last two parameters
sparse_cb_info.total_write_count = sparse_file_len(s_file, true, false);

num_sectors = (sparse_cb_info.total_write_count + program->sector_size - 1) / program->sector_size;
} else {
num_sectors = (sb.st_size + program->sector_size - 1) / program->sector_size;
}

if (program->num_sectors && num_sectors > program->num_sectors) {
fprintf(stderr, "[PROGRAM] %s truncated to %d\n",
Expand Down Expand Up @@ -388,26 +465,39 @@ 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);
left = num_sectors;
while (left > 0) {
chunk_size = MIN(max_payload_size / program->sector_size, left);
if (program->is_sparse) {
sparse_cb_info.payload = buf;
sparse_cb_info.payload_len = 0U;
sparse_cb_info.write_count = 0U;
sparse_cb_info.program = program;
sparse_cb_info.qdl = qdl;

n = read(fd, buf, chunk_size * program->sector_size);
if (n < 0)
err(1, "failed to read");
// TODO originally used sparse_stream_callback
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently it times out.

//ret = sparse_stream_callback(s_file, firehose_program_sparse, &sparse_cb_info);
ret = sparse_file_foreach_chunk(s_file, true, false, firehose_program_sparse, &sparse_cb_info);

if (n < max_payload_size)
memset(buf + n, 0, max_payload_size - n);
sparse_file_destroy(s_file);
} else {
left = num_sectors;
while (left > 0) {
chunk_size = MIN(max_payload_size / program->sector_size, left);

n = qdl_write(qdl, buf, chunk_size * program->sector_size);
if (n < 0)
err(1, "failed to write");
n = read(fd, buf, chunk_size * program->sector_size);
if (n < 0)
err(1, "failed to read");

if (n != chunk_size * program->sector_size)
err(1, "failed to write full sector");
if (n < max_payload_size)
memset(buf + n, 0, max_payload_size - n);

left -= chunk_size;
n = qdl_write(qdl, buf, chunk_size * program->sector_size);
if (n < 0)
err(1, "failed to write");

if (n != chunk_size * program->sector_size)
err(1, "failed to write full sector");

left -= chunk_size;
}
}

t = time(NULL) - t0;
Expand All @@ -427,6 +517,8 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int

out:
xmlFreeDoc(doc);
// Not present before.
//free(buf);
return ret;
}

Expand Down
6 changes: 6 additions & 0 deletions program.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static int load_program_tag(xmlNode *node, bool is_nand)
{
struct program *program;
int errors = 0;
const char *sparse;

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

Expand All @@ -102,6 +103,11 @@ static int load_program_tag(xmlNode *node, bool is_nand)
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
}

sparse = attr_as_string(node, "sparse", &errors);
if (!errors) {
program->is_sparse = strcmp(sparse, "true") == 0;
}

if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
free(program);
Expand Down
1 change: 1 addition & 0 deletions program.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct program {
const char *label;
unsigned num_sectors;
unsigned partition;
bool is_sparse;
const char *start_sector;
unsigned last_sector;

Expand Down