am 8a8e6cc3: (cherry-pick) EMMC support in applypatch
Merge commit '8a8e6cc33cc0dcfe7e184b59d5ef1f1c29095509' into gingerbread-plus-aosp * commit '8a8e6cc33cc0dcfe7e184b59d5ef1f1c29095509': (cherry-pick) EMMC support in applypatch
This commit is contained in:
+180
-107
@@ -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