(cherry-pick) EMMC support in applypatch
Let applypatch read and write EMMC partitions as well as MTD ones. This enables incremental updates that include boot image changes, as well as OTA of new recovery partitions. Change-Id: Ib1861219c7ca66dff29ad02d6a0a14e5f03eb4d8
This commit is contained in:
@@ -30,10 +30,10 @@
|
|||||||
#include "mtdutils/mtdutils.h"
|
#include "mtdutils/mtdutils.h"
|
||||||
#include "edify/expr.h"
|
#include "edify/expr.h"
|
||||||
|
|
||||||
int SaveFileContents(const char* filename, FileContents file);
|
static int SaveFileContents(const char* filename, FileContents file);
|
||||||
int LoadMTDContents(const char* filename, FileContents* file);
|
static int LoadPartitionContents(const char* filename, FileContents* file);
|
||||||
int ParseSha1(const char* str, uint8_t* digest);
|
int ParseSha1(const char* str, uint8_t* digest);
|
||||||
ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
|
static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
|
||||||
|
|
||||||
static int mtd_partitions_scanned = 0;
|
static int mtd_partitions_scanned = 0;
|
||||||
|
|
||||||
@@ -42,10 +42,11 @@ static int mtd_partitions_scanned = 0;
|
|||||||
int LoadFileContents(const char* filename, FileContents* file) {
|
int LoadFileContents(const char* filename, FileContents* file) {
|
||||||
file->data = NULL;
|
file->data = NULL;
|
||||||
|
|
||||||
// A special 'filename' beginning with "MTD:" means to load the
|
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
|
||||||
// contents of an MTD partition.
|
// load the contents of a partition.
|
||||||
if (strncmp(filename, "MTD:", 4) == 0) {
|
if (strncmp(filename, "MTD:", 4) == 0 ||
|
||||||
return LoadMTDContents(filename, file);
|
strncmp(filename, "EMMC:", 5) == 0) {
|
||||||
|
return LoadPartitionContents(filename, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(filename, &file->st) != 0) {
|
if (stat(filename, &file->st) != 0) {
|
||||||
@@ -98,26 +99,35 @@ void FreeFileContents(FileContents* file) {
|
|||||||
free(file);
|
free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the contents of an MTD partition into the provided
|
// Load the contents of an MTD or EMMC partition into the provided
|
||||||
// FileContents. filename should be a string of the form
|
// FileContents. filename should be a string of the form
|
||||||
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
|
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..." (or
|
||||||
// The smallest size_n bytes for which that prefix of the mtd contents
|
// "EMMC:<partition_device>:..."). The smallest size_n bytes for
|
||||||
// has the corresponding sha1 hash will be loaded. It is acceptable
|
// which that prefix of the partition contents has the corresponding
|
||||||
// for a size value to be repeated with different sha1s. Will return
|
// sha1 hash will be loaded. It is acceptable for a size value to be
|
||||||
// 0 on success.
|
// repeated with different sha1s. Will return 0 on success.
|
||||||
//
|
//
|
||||||
// This complexity is needed because if an OTA installation is
|
// This complexity is needed because if an OTA installation is
|
||||||
// interrupted, the partition might contain either the source or the
|
// interrupted, the partition might contain either the source or the
|
||||||
// target data, which might be of different lengths. We need to know
|
// target data, which might be of different lengths. We need to know
|
||||||
// the length in order to read from MTD (there is no "end-of-file"
|
// the length in order to read from a partition (there is no
|
||||||
// marker), so the caller must specify the possible lengths and the
|
// "end-of-file" marker), so the caller must specify the possible
|
||||||
// hash of the data, and we'll do the load expecting to find one of
|
// lengths and the hash of the data, and we'll do the load expecting
|
||||||
// those hashes.
|
// to find one of those hashes.
|
||||||
int LoadMTDContents(const char* filename, FileContents* file) {
|
enum PartitionType { MTD, EMMC };
|
||||||
|
|
||||||
|
static int LoadPartitionContents(const char* filename, FileContents* file) {
|
||||||
char* copy = strdup(filename);
|
char* copy = strdup(filename);
|
||||||
const char* magic = strtok(copy, ":");
|
const char* magic = strtok(copy, ":");
|
||||||
if (strcmp(magic, "MTD") != 0) {
|
|
||||||
printf("LoadMTDContents called with bad filename (%s)\n",
|
enum PartitionType type;
|
||||||
|
|
||||||
|
if (strcmp(magic, "MTD") == 0) {
|
||||||
|
type = MTD;
|
||||||
|
} else if (strcmp(magic, "EMMC") == 0) {
|
||||||
|
type = EMMC;
|
||||||
|
} else {
|
||||||
|
printf("LoadPartitionContents called with bad filename (%s)\n",
|
||||||
filename);
|
filename);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -131,7 +141,7 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (colons < 3 || colons%2 == 0) {
|
if (colons < 3 || colons%2 == 0) {
|
||||||
printf("LoadMTDContents called with bad filename (%s)\n",
|
printf("LoadPartitionContents called with bad filename (%s)\n",
|
||||||
filename);
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +154,7 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
const char* size_str = strtok(NULL, ":");
|
const char* size_str = strtok(NULL, ":");
|
||||||
size[i] = strtol(size_str, NULL, 10);
|
size[i] = strtol(size_str, NULL, 10);
|
||||||
if (size[i] == 0) {
|
if (size[i] == 0) {
|
||||||
printf("LoadMTDContents called with bad size (%s)\n", filename);
|
printf("LoadPartitionContents called with bad size (%s)\n", filename);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sha1sum[i] = strtok(NULL, ":");
|
sha1sum[i] = strtok(NULL, ":");
|
||||||
@@ -156,23 +166,38 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
size_array = size;
|
size_array = size;
|
||||||
qsort(index, pairs, sizeof(int), compare_size_indices);
|
qsort(index, pairs, sizeof(int), compare_size_indices);
|
||||||
|
|
||||||
if (!mtd_partitions_scanned) {
|
MtdReadContext* ctx = NULL;
|
||||||
mtd_scan_partitions();
|
FILE* dev = NULL;
|
||||||
mtd_partitions_scanned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
switch (type) {
|
||||||
if (mtd == NULL) {
|
case MTD:
|
||||||
printf("mtd partition \"%s\" not found (loading %s)\n",
|
if (!mtd_partitions_scanned) {
|
||||||
partition, filename);
|
mtd_scan_partitions();
|
||||||
return -1;
|
mtd_partitions_scanned = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MtdReadContext* ctx = mtd_read_partition(mtd);
|
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||||
if (ctx == NULL) {
|
if (mtd == NULL) {
|
||||||
printf("failed to initialize read of mtd partition \"%s\"\n",
|
printf("mtd partition \"%s\" not found (loading %s)\n",
|
||||||
partition);
|
partition, filename);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = mtd_read_partition(mtd);
|
||||||
|
if (ctx == NULL) {
|
||||||
|
printf("failed to initialize read of mtd partition \"%s\"\n",
|
||||||
|
partition);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMMC:
|
||||||
|
dev = fopen(partition, "rb");
|
||||||
|
if (dev == NULL) {
|
||||||
|
printf("failed to open emmc partition \"%s\": %s\n",
|
||||||
|
partition, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SHA_CTX sha_ctx;
|
SHA_CTX sha_ctx;
|
||||||
@@ -191,7 +216,15 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
size_t next = size[index[i]] - file->size;
|
size_t next = size[index[i]] - file->size;
|
||||||
size_t read = 0;
|
size_t read = 0;
|
||||||
if (next > 0) {
|
if (next > 0) {
|
||||||
read = mtd_read_data(ctx, p, next);
|
switch (type) {
|
||||||
|
case MTD:
|
||||||
|
read = mtd_read_data(ctx, p, next);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMMC:
|
||||||
|
read = fread(p, 1, next, dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (next != read) {
|
if (next != read) {
|
||||||
printf("short read (%d bytes of %d) for partition \"%s\"\n",
|
printf("short read (%d bytes of %d) for partition \"%s\"\n",
|
||||||
read, next, partition);
|
read, next, partition);
|
||||||
@@ -220,7 +253,7 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
|
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
|
||||||
// we have a match. stop reading the partition; we'll return
|
// we have a match. stop reading the partition; we'll return
|
||||||
// the data we've read so far.
|
// the data we've read so far.
|
||||||
printf("mtd read matched size %d sha %s\n",
|
printf("partition read matched size %d sha %s\n",
|
||||||
size[index[i]], sha1sum[index[i]]);
|
size[index[i]], sha1sum[index[i]]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -228,12 +261,21 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
p += read;
|
p += read;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtd_read_close(ctx);
|
switch (type) {
|
||||||
|
case MTD:
|
||||||
|
mtd_read_close(ctx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMMC:
|
||||||
|
fclose(dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (i == pairs) {
|
if (i == pairs) {
|
||||||
// Ran off the end of the list of (size,sha1) pairs without
|
// Ran off the end of the list of (size,sha1) pairs without
|
||||||
// finding a match.
|
// finding a match.
|
||||||
printf("contents of MTD partition \"%s\" didn't match %s\n",
|
printf("contents of partition \"%s\" didn't match %s\n",
|
||||||
partition, filename);
|
partition, filename);
|
||||||
free(file->data);
|
free(file->data);
|
||||||
file->data = NULL;
|
file->data = NULL;
|
||||||
@@ -261,7 +303,7 @@ int LoadMTDContents(const char* filename, FileContents* file) {
|
|||||||
|
|
||||||
// Save the contents of the given FileContents object under the given
|
// Save the contents of the given FileContents object under the given
|
||||||
// filename. Return 0 on success.
|
// filename. Return 0 on success.
|
||||||
int SaveFileContents(const char* filename, FileContents file) {
|
static int SaveFileContents(const char* filename, FileContents file) {
|
||||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
printf("failed to open \"%s\" for write: %s\n",
|
printf("failed to open \"%s\" for write: %s\n",
|
||||||
@@ -292,61 +334,87 @@ int SaveFileContents(const char* filename, FileContents file) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a memory buffer to target_mtd partition, a string of the form
|
// Write a memory buffer to 'target' partition, a string of the form
|
||||||
// "MTD:<partition>[:...]". Return 0 on success.
|
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:". Return 0 on
|
||||||
int WriteToMTDPartition(unsigned char* data, size_t len,
|
// success.
|
||||||
const char* target_mtd) {
|
int WriteToPartition(unsigned char* data, size_t len,
|
||||||
char* partition = strchr(target_mtd, ':');
|
const char* target) {
|
||||||
|
char* copy = strdup(target);
|
||||||
|
const char* magic = strtok(copy, ":");
|
||||||
|
|
||||||
|
enum PartitionType type;
|
||||||
|
if (strcmp(magic, "MTD") == 0) {
|
||||||
|
type = MTD;
|
||||||
|
} else if (strcmp(magic, "EMMC") == 0) {
|
||||||
|
type = EMMC;
|
||||||
|
} else {
|
||||||
|
printf("WriteToPartition called with bad target (%s)\n", target);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char* partition = strtok(NULL, ":");
|
||||||
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
printf("bad MTD target name \"%s\"\n", target_mtd);
|
printf("bad partition target name \"%s\"\n", target);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
++partition;
|
|
||||||
// Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
|
|
||||||
// We want just the partition name "boot".
|
|
||||||
partition = strdup(partition);
|
|
||||||
char* end = strchr(partition, ':');
|
|
||||||
if (end != NULL)
|
|
||||||
*end = '\0';
|
|
||||||
|
|
||||||
if (!mtd_partitions_scanned) {
|
|
||||||
mtd_scan_partitions();
|
|
||||||
mtd_partitions_scanned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
|
||||||
if (mtd == NULL) {
|
|
||||||
printf("mtd partition \"%s\" not found for writing\n", partition);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
switch (type) {
|
||||||
if (ctx == NULL) {
|
case MTD:
|
||||||
printf("failed to init mtd partition \"%s\" for writing\n",
|
if (!mtd_partitions_scanned) {
|
||||||
partition);
|
mtd_scan_partitions();
|
||||||
return -1;
|
mtd_partitions_scanned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||||
|
if (mtd == NULL) {
|
||||||
|
printf("mtd partition \"%s\" not found for writing\n",
|
||||||
|
partition);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
||||||
|
if (ctx == NULL) {
|
||||||
|
printf("failed to init mtd partition \"%s\" for writing\n",
|
||||||
|
partition);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t written = mtd_write_data(ctx, (char*)data, len);
|
||||||
|
if (written != len) {
|
||||||
|
printf("only wrote %d of %d bytes to MTD %s\n",
|
||||||
|
written, len, partition);
|
||||||
|
mtd_write_close(ctx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtd_erase_blocks(ctx, -1) < 0) {
|
||||||
|
printf("error finishing mtd write of %s\n", partition);
|
||||||
|
mtd_write_close(ctx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtd_write_close(ctx)) {
|
||||||
|
printf("error closing mtd write of %s\n", partition);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMMC:
|
||||||
|
;
|
||||||
|
FILE* f = fopen(partition, "wb");
|
||||||
|
if (fwrite(data, 1, len, f) != len) {
|
||||||
|
printf("short write writing to %s (%s)\n",
|
||||||
|
partition, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fclose(f) != 0) {
|
||||||
|
printf("error closing %s (%s)\n", partition, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t written = mtd_write_data(ctx, (char*)data, len);
|
free(copy);
|
||||||
if (written != len) {
|
|
||||||
printf("only wrote %d of %d bytes to MTD %s\n",
|
|
||||||
written, len, partition);
|
|
||||||
mtd_write_close(ctx);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtd_erase_blocks(ctx, -1) < 0) {
|
|
||||||
printf("error finishing mtd write of %s\n", partition);
|
|
||||||
mtd_write_close(ctx);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtd_write_close(ctx)) {
|
|
||||||
printf("error closing mtd write of %s\n", partition);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(partition);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +474,7 @@ int applypatch_check(const char* filename,
|
|||||||
file.data = NULL;
|
file.data = NULL;
|
||||||
|
|
||||||
// It's okay to specify no sha1s; the check will pass if the
|
// It's okay to specify no sha1s; the check will pass if the
|
||||||
// LoadFileContents is successful. (Useful for reading MTD
|
// LoadFileContents is successful. (Useful for reading
|
||||||
// partitions, where the filename encodes the sha1s; no need to
|
// partitions, where the filename encodes the sha1s; no need to
|
||||||
// check them twice.)
|
// check them twice.)
|
||||||
if (LoadFileContents(filename, &file) != 0 ||
|
if (LoadFileContents(filename, &file) != 0 ||
|
||||||
@@ -518,8 +586,8 @@ int CacheSizeCheck(size_t bytes) {
|
|||||||
// - otherwise, or if any error is encountered, exits with non-zero
|
// - otherwise, or if any error is encountered, exits with non-zero
|
||||||
// status.
|
// status.
|
||||||
//
|
//
|
||||||
// <source_filename> may refer to an MTD partition to read the source
|
// <source_filename> may refer to a partition to read the source data.
|
||||||
// data. See the comments for the LoadMTDContents() function above
|
// See the comments for the LoadPartition Contents() function above
|
||||||
// for the format of such a filename.
|
// for the format of such a filename.
|
||||||
|
|
||||||
int applypatch(const char* source_filename,
|
int applypatch(const char* source_filename,
|
||||||
@@ -623,14 +691,16 @@ int applypatch(const char* source_filename,
|
|||||||
// Is there enough room in the target filesystem to hold the patched
|
// Is there enough room in the target filesystem to hold the patched
|
||||||
// file?
|
// file?
|
||||||
|
|
||||||
if (strncmp(target_filename, "MTD:", 4) == 0) {
|
if (strncmp(target_filename, "MTD:", 4) == 0 ||
|
||||||
// If the target is an MTD partition, we're actually going to
|
strncmp(target_filename, "EMMC:", 5) == 0) {
|
||||||
// write the output to /tmp and then copy it to the partition.
|
// If the target is a partition, we're actually going to
|
||||||
// statfs() always returns 0 blocks free for /tmp, so instead
|
// write the output to /tmp and then copy it to the
|
||||||
// we'll just assume that /tmp has enough space to hold the file.
|
// partition. statfs() always returns 0 blocks free for
|
||||||
|
// /tmp, so instead we'll just assume that /tmp has enough
|
||||||
|
// space to hold the file.
|
||||||
|
|
||||||
// We still write the original source to cache, in case the MTD
|
// We still write the original source to cache, in case
|
||||||
// write is interrupted.
|
// the partition write is interrupted.
|
||||||
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
|
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
|
||||||
printf("not enough free space on /cache\n");
|
printf("not enough free space on /cache\n");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -661,11 +731,13 @@ int applypatch(const char* source_filename,
|
|||||||
// copy the source file to cache, then delete it from the original
|
// copy the source file to cache, then delete it from the original
|
||||||
// location.
|
// location.
|
||||||
|
|
||||||
if (strncmp(source_filename, "MTD:", 4) == 0) {
|
if (strncmp(source_filename, "MTD:", 4) == 0 ||
|
||||||
|
strncmp(source_filename, "EMMC:", 5) == 0) {
|
||||||
// It's impossible to free space on the target filesystem by
|
// It's impossible to free space on the target filesystem by
|
||||||
// deleting the source if the source is an MTD partition. If
|
// deleting the source if the source is a partition. If
|
||||||
// we're ever in a state where we need to do this, fail.
|
// we're ever in a state where we need to do this, fail.
|
||||||
printf("not enough free space for target but source is MTD\n");
|
printf("not enough free space for target but source "
|
||||||
|
"is partition\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,7 +776,8 @@ int applypatch(const char* source_filename,
|
|||||||
void* token = NULL;
|
void* token = NULL;
|
||||||
output = -1;
|
output = -1;
|
||||||
outname = NULL;
|
outname = NULL;
|
||||||
if (strncmp(target_filename, "MTD:", 4) == 0) {
|
if (strncmp(target_filename, "MTD:", 4) == 0 ||
|
||||||
|
strncmp(target_filename, "EMMC:", 5) == 0) {
|
||||||
// We store the decoded output in memory.
|
// We store the decoded output in memory.
|
||||||
msi.buffer = malloc(target_size);
|
msi.buffer = malloc(target_size);
|
||||||
if (msi.buffer == NULL) {
|
if (msi.buffer == NULL) {
|
||||||
@@ -780,8 +853,8 @@ int applypatch(const char* source_filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (output < 0) {
|
if (output < 0) {
|
||||||
// Copy the temp file to the MTD partition.
|
// Copy the temp file to the partition.
|
||||||
if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
|
if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) {
|
||||||
printf("write of patched data to %s failed\n", target_filename);
|
printf("write of patched data to %s failed\n", target_filename);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user