summaryrefslogtreecommitdiffstats
path: root/updater/install.c
diff options
context:
space:
mode:
Diffstat (limited to 'updater/install.c')
-rw-r--r--updater/install.c263
1 files changed, 235 insertions, 28 deletions
diff --git a/updater/install.c b/updater/install.c
index 872cbf857..8defc7720 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -45,11 +45,27 @@
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "updater.h"
+#include "syspatch.h"
+#include "install.h"
#ifdef USE_EXT4
#include "make_ext4fs.h"
+#include "wipe.h"
#endif
+// Take a sha-1 digest and return it as a newly-allocated hex string.
+static char* PrintSha1(const uint8_t* digest) {
+ char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
+ int i;
+ const char* alphabet = "0123456789abcdef";
+ for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+ buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
+ buffer[i*2+1] = alphabet[digest[i] & 0xf];
+ }
+ buffer[i*2] = '\0';
+ return buffer;
+}
+
// mount(fs_type, partition_type, location, mount_point)
//
// fs_type="yaffs2" partition_type="MTD" location=partition
@@ -414,6 +430,54 @@ Value* PackageExtractDirFn(const char* name, State* state,
}
+DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) {
+ const char* name = "ReadDontCareMapFromZip";
+
+ const ZipEntry* entry = mzFindZipEntry(za, path);
+ if (entry == NULL) {
+ printf("%s: no %s in package\n", name, path);
+ return NULL;
+ }
+
+ size_t map_size = mzGetZipEntryUncompLen(entry);
+ char* map_data = malloc(map_size);
+ if (map_data == NULL) {
+ printf("%s: failed to allocate %zu bytes for %s\n",
+ name, map_size, path);
+ return NULL;
+ }
+
+ if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) {
+ printf("%s: failed to read %s\n", name, path);
+ return NULL;
+ }
+
+ char* p = map_data;
+ DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap));
+
+ map->block_size = strtoul(p, &p, 0);
+ if (map->block_size != 4096) {
+ printf("%s: unexpected block size %zu\n", name, map->block_size);
+ return NULL;
+ }
+
+ map->region_count = strtoul(p, &p, 0);
+ map->regions = (int*) malloc(map->region_count * sizeof(int));
+
+ int i;
+ for (i = 0; i < map->region_count; ++i) {
+ map->regions[i] = strtoul(p, &p, 0);
+ }
+
+ return map;
+}
+
+bool MapWriter(const unsigned char* data, int dataLen, void* cookie) {
+ return write_with_map(data, dataLen, (MapState*) cookie) == dataLen;
+}
+
+// package_extract_file(package_path, destination_path, map_path)
+// or
// package_extract_file(package_path, destination_path)
// or
// package_extract_file(package_path)
@@ -421,19 +485,30 @@ Value* PackageExtractDirFn(const char* name, State* state,
// function (the char* returned is actually a FileContents*).
Value* PackageExtractFileFn(const char* name, State* state,
int argc, Expr* argv[]) {
- if (argc != 1 && argc != 2) {
- return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
+ if (argc < 1 || argc > 3) {
+ return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d",
name, argc);
}
bool success = false;
- if (argc == 2) {
- // The two-argument version extracts to a file.
+ if (argc >= 2) {
+ // The two-argument version extracts to a file; the three-arg
+ // version extracts to a file, skipping over regions in a
+ // don't care map.
+
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
char* zip_path;
char* dest_path;
- if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+ char* map_path = NULL;
+ DontCareMap* map = NULL;
+ if (argc == 2) {
+ if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+ } else {
+ if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL;
+ map = ReadDontCareMapFromZip(za, map_path);
+ if (map == NULL) goto done2;
+ }
- ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
printf("%s: no %s in package\n", name, zip_path);
@@ -446,12 +521,26 @@ Value* PackageExtractFileFn(const char* name, State* state,
name, dest_path, strerror(errno));
goto done2;
}
- success = mzExtractZipEntryToFile(za, entry, fileno(f));
+ if (map) {
+ MapState state;
+ state.map = map;
+ state.cr = 0;
+ state.so_far = 0;
+ state.f = f;
+ success = mzProcessZipEntryContents(za, entry, MapWriter, &state);
+ } else {
+ success = mzExtractZipEntryToFile(za, entry, fileno(f));
+ }
fclose(f);
done2:
free(zip_path);
free(dest_path);
+ free(map_path);
+ if (map) {
+ free(map->regions);
+ free(map);
+ }
return StringValue(strdup(success ? "t" : ""));
} else {
// The one-argument version returns the contents of the file
@@ -838,8 +927,8 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
-// per line, # comment lines and blank lines okay), and returns the value
-// for 'key' (or "" if it isn't defined).
+// per line. # comment lines,blank lines, lines without '=' ignored),
+// and returns the value for 'key' (or "" if it isn't defined).
Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* buffer = NULL;
@@ -897,9 +986,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* equal = strchr(line, '=');
if (equal == NULL) {
- ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
- name, line, filename);
- goto done;
+ continue;
}
// trim whitespace between key and '='
@@ -1053,8 +1140,117 @@ Value* ApplyPatchSpaceFn(const char* name, State* state,
return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
}
+bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) {
+ MapState state;
+
+ state.f = f;
+ state.so_far = 0;
+ state.cr = 0;
+ state.map = map;
+
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+
+ unsigned char buffer[32173];
+ size_t bytes_read;
+
+ while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) {
+ SHA_update(&ctx, buffer, bytes_read);
+ }
+ const uint8_t* digest = SHA_final(&ctx);
+
+ return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0;
+}
+
+
+// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch)
+
+Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 6) {
+ return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* target_mapfilename;
+ char* target_sha1;
+ char* init_mapfilename;
+ char* init_sha1;
+ char* patch_filename;
+ uint8_t target_digest[SHA_DIGEST_SIZE];
+ uint8_t init_digest[SHA_DIGEST_SIZE];
+
+ if (ReadArgs(state, argv, 6, &filename,
+ &target_mapfilename, &target_sha1,
+ &init_mapfilename, &init_sha1, &patch_filename) < 0) {
+ return NULL;
+ }
+
+ if (ParseSha1(target_sha1, target_digest) != 0) {
+ printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1);
+ memset(target_digest, 0, SHA_DIGEST_SIZE);
+ }
+ if (ParseSha1(init_sha1, init_digest) != 0) {
+ printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1);
+ memset(init_digest, 0, SHA_DIGEST_SIZE);
+ }
+
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+ FILE* src = fopen(filename, "r");
+
+ DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename);
+ if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name);
+ DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename);
+ if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name);
+
+ if (CheckMappedFileSha1(src, init_map, init_digest)) {
+ // If the partition contents match the init_digest, then we need to apply the patch.
+
+ rewind(src);
+
+ const ZipEntry* entry = mzFindZipEntry(za, patch_filename);
+ if (entry == NULL) {
+ return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename);
+ }
+
+ unsigned char* patch_data;
+ size_t patch_len;
+ if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) {
+ return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename);
+ }
+
+ FILE* tgt = fopen(filename, "r+");
+
+ int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map);
+
+ fclose(src);
+ fclose(tgt);
+
+ if (ret != 0) {
+ return ErrorAbort(state, "%s(): patching failed\n", name);
+ }
+ } else {
+ rewind(src);
+ if (CheckMappedFileSha1(src, target_map, target_digest)) {
+ // If the partition contents match the target already, we
+ // don't need to do anything.
+ printf("%s: output is already target\n", name);
+ } else {
+ return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename);
+ }
+ }
+
+ done:
+ free(target_sha1);
+ free(target_mapfilename);
+ free(init_sha1);
+ free(init_mapfilename);
+ free(patch_filename);
+ return StringValue(filename);
+
+}
+
+// apply_patch(file, size, init_sha1, tgt_sha1, patch)
-// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 6 || (argc % 2) == 1) {
return ErrorAbort(state, "%s(): expected at least 6 args and an "
@@ -1239,19 +1435,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(buffer));
}
-// Take a sha-1 digest and return it as a newly-allocated hex string.
-static char* PrintSha1(uint8_t* digest) {
- char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
- int i;
- const char* alphabet = "0123456789abcdef";
- for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
- buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
- buffer[i*2+1] = alphabet[digest[i] & 0xf];
- }
- buffer[i*2] = '\0';
- return buffer;
-}
-
// sha1_check(data)
// to return the sha1 of the data (given in the format returned by
// read_file).
@@ -1322,7 +1505,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
v->type = VAL_BLOB;
FileContents fc;
- if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
+ if (LoadFileContents(filename, &fc) != 0) {
free(filename);
v->size = -1;
v->data = NULL;
@@ -1419,7 +1602,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
// Return the value most recently saved with SetStageFn. The argument
// is the block device for the misc partition.
Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
- if (argc != 2) {
+ if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
@@ -1436,6 +1619,27 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(buffer));
}
+Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* len_str;
+ if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL;
+
+ size_t len = strtoull(len_str, NULL, 0);
+ int fd = open(filename, O_WRONLY, 0644);
+ int success = wipe_block_device(fd, len);
+
+ free(filename);
+ free(len_str);
+
+ close(fd);
+
+ return StringValue(strdup(success ? "t" : ""));
+}
+
void RegisterInstallFunctions() {
RegisterFunction("mount", MountFn);
RegisterFunction("is_mounted", IsMountedFn);
@@ -1469,6 +1673,9 @@ void RegisterInstallFunctions() {
RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
+ RegisterFunction("wipe_block_device", WipeBlockDeviceFn);
+ RegisterFunction("syspatch", SysPatchFn);
+
RegisterFunction("read_file", ReadFileFn);
RegisterFunction("sha1_check", Sha1CheckFn);
RegisterFunction("rename", RenameFn);