add support sparse attribute

Signed-off-by: Maxim Paymushkin <maxim.paymushkin.development@gmail.com>
This commit is contained in:
Maxim Paymushkin
2025-03-27 15:41:04 +01:00
committed by Maxim Paymushkin
parent 4288cebfdb
commit 02c008adfd
6 changed files with 335 additions and 17 deletions

View File

@@ -4,9 +4,12 @@ VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/
CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
ifeq ($(OS),Windows_NT)
LDFLAGS += -lws2_32
endif
prefix := /usr/local
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c sparse.c
QDL_OBJS := $(QDL_SRCS:.c=.o)
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c

View File

@@ -26,6 +26,7 @@
#include "ufs.h"
#include "oscompat.h"
#include "vip.h"
#include "sparse.h"
enum {
FIREHOSE_ACK = 0,
@@ -366,6 +367,7 @@ 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;
@@ -373,14 +375,16 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
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(qdl->max_payload_size);
@@ -418,7 +422,24 @@ 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 < qdl->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;
ux_debug("FIREHOSE RAW BINARY WRITE: %s, %d bytes\n",
@@ -432,14 +453,16 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
vip_gen_chunk_init(qdl);
chunk_size = MIN(qdl->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 < qdl->max_payload_size)
memset(buf + n, 0, qdl->max_payload_size - n);
if (n < qdl->max_payload_size)
memset(buf + n, 0, qdl->max_payload_size - n);
}
vip_gen_chunk_update(qdl, buf, chunk_size * program->sector_size);

109
program.c
View File

@@ -15,6 +15,7 @@
#include "program.h"
#include "qdl.h"
#include "oscompat.h"
#include "sparse.h"
static struct program *programes;
static struct program *programes_last;
@@ -54,6 +55,97 @@ 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 int 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 ((off_t)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;
@@ -138,6 +230,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;
@@ -166,7 +259,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)

View File

@@ -20,6 +20,9 @@ struct program {
bool is_nand;
bool is_erase;
unsigned int sparse_chunk_type;
unsigned int sparse_chunk_data;
struct program *next;
};

118
sparse.c Normal file
View File

@@ -0,0 +1,118 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2025, Maksim Paimushkin <maxim.paymushkin.development@gmail.com>
* All rights reserved.
*/
#define _FILE_OFFSET_BITS 64
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "sparse.h"
#include "qdl.h"
int sparse_header_parse(int fd, sparse_header_t *sparse_header)
{
lseek(fd, 0, SEEK_SET);
if (read(fd, sparse_header, sizeof(sparse_header_t)) != sizeof(sparse_header_t)) {
ux_err("[SPARSE] Unable to read sparse header\n");
return -EINVAL;
}
if (ntohl(sparse_header->magic) != ntohl(SPARSE_HEADER_MAGIC)) {
ux_err("[SPARSE] Invalid magic in sparse header\n");
return -EINVAL;
}
if (ntohs(sparse_header->major_version) != ntohs(SPARSE_HEADER_MAJOR_VER)) {
ux_err("[SPARSE] Invalid major version in sparse header\n");
return -EINVAL;
}
if (ntohs(sparse_header->minor_version) != ntohs(SPARSE_HEADER_MINOR_VER)) {
ux_err("[SPARSE] Invalid minor version in sparse header\n");
return -EINVAL;
}
if (sparse_header->file_hdr_sz > sizeof(sparse_header_t))
lseek(fd, sparse_header->file_hdr_sz - sizeof(sparse_header_t), SEEK_CUR);
return 0;
}
int sparse_chunk_header_parse(int fd, sparse_header_t *sparse_header,
unsigned int *chunk_size, unsigned int *value)
{
chunk_header_t chunk_header;
uint32_t fill_value = 0;
*chunk_size = 0;
*value = 0;
if (read(fd, &chunk_header, sizeof(chunk_header_t)) != sizeof(chunk_header_t)) {
ux_err("[SPARSE] Unable to read sparse chunk header\n");
return -EINVAL;
}
if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t))
lseek(fd, sparse_header->chunk_hdr_sz - sizeof(chunk_header_t), SEEK_CUR);
if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_RAW)) {
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
if (chunk_header.total_sz != (sparse_header->chunk_hdr_sz + *chunk_size)) {
ux_err("[SPARSE] Bogus chunk size, type Raw\n");
return -EINVAL;
}
/* Save the current file offset in the 'value' variable */
*value = lseek(fd, 0, SEEK_CUR);
/* Move the file cursor forward by the size of the chunk */
lseek(fd, *chunk_size, SEEK_CUR);
return CHUNK_TYPE_RAW;
} else if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_DONT_CARE)) {
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
if (chunk_header.total_sz != sparse_header->chunk_hdr_sz) {
ux_err("[SPARSE] Bogus chunk size, type Don't Care\n");
return -EINVAL;
}
return CHUNK_TYPE_DONT_CARE;
} else if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_FILL)) {
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
if (chunk_header.total_sz != (sparse_header->chunk_hdr_sz + sizeof(fill_value))) {
ux_err("[SPARSE] Bogus chunk size, type Fill\n");
return -EINVAL;
}
if (read(fd, &fill_value, sizeof(fill_value)) != sizeof(fill_value)) {
ux_err("[SPARSE] Unable to read fill value\n");
return -EINVAL;
}
/* Save the current fill value in the 'value' variable */
*value = fill_value;
return CHUNK_TYPE_FILL;
}
ux_err("[SPARSE] Unknown chunk\n");
return -EINVAL;
}

64
sparse.h Normal file
View File

@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) 2010 The Android Open Source Project
*/
#ifndef __SPARSE_H__
#define __SPARSE_H__
#include <stdint.h>
typedef struct __attribute__((__packed__)) sparse_header {
/* 0xed26ff3a */
uint32_t magic;
/* (0x1) - reject images with higher major versions */
uint16_t major_version;
/* (0x0) - allow images with higer minor versions */
uint16_t minor_version;
/* 28 bytes for first revision of the file format */
uint16_t file_hdr_sz;
/* 12 bytes for first revision of the file format */
uint16_t chunk_hdr_sz;
/* block size in bytes, must be a multiple of 4 (4096) */
uint32_t blk_sz;
/* total blocks in the non-sparse output image */
uint32_t total_blks;
/* total chunks in the sparse input image */
uint32_t total_chunks;
/*
* CRC32 checksum of the original data, counting "don't care"
* as 0. Standard 802.3 polynomial, use a Public Domain
* table implementation
*/
uint32_t image_checksum;
} sparse_header_t;
#define SPARSE_HEADER_MAGIC 0xed26ff3a
#define SPARSE_HEADER_MAJOR_VER 0x0001
#define SPARSE_HEADER_MINOR_VER 0x0000
typedef struct __attribute__((__packed__)) chunk_header {
uint16_t chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
uint16_t reserved1;
uint32_t chunk_sz; /* in blocks in output image */
uint32_t total_sz; /* in bytes of chunk input file including chunk header and data */
} chunk_header_t;
#define CHUNK_TYPE_RAW 0xCAC1
#define CHUNK_TYPE_FILL 0xCAC2
#define CHUNK_TYPE_DONT_CARE 0xCAC3
/*
* Parses the sparse image header from the file descriptor.
* Returns 0 on success, or an error code otherwise.
*/
int sparse_header_parse(int fd, sparse_header_t *sparse_header);
/*
* Parses the sparse image chunk header from the file descriptor.
* Sets the chunk size and value based on the parsed data.
* Returns the chunk type on success, or an error code otherwise.
*/
int sparse_chunk_header_parse(int fd, sparse_header_t *sparse_header,
unsigned int *chunk_size, unsigned int *value);
#endif