Home | History | Annotate | Download | only in updater
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <ctype.h>
     18 #include <errno.h>
     19 #include <stdarg.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <sys/mount.h>
     24 #include <sys/stat.h>
     25 #include <sys/types.h>
     26 #include <sys/wait.h>
     27 #include <unistd.h>
     28 #include <fcntl.h>
     29 #include <time.h>
     30 
     31 #include "cutils/misc.h"
     32 #include "cutils/properties.h"
     33 #include "edify/expr.h"
     34 #include "mincrypt/sha.h"
     35 #include "minzip/DirUtil.h"
     36 #include "minelf/Retouch.h"
     37 #include "mtdutils/mounts.h"
     38 #include "mtdutils/mtdutils.h"
     39 #include "updater.h"
     40 #include "applypatch/applypatch.h"
     41 
     42 #ifdef USE_EXT4
     43 #include "make_ext4fs.h"
     44 #endif
     45 
     46 // mount(fs_type, partition_type, location, mount_point)
     47 //
     48 //    fs_type="yaffs2" partition_type="MTD"     location=partition
     49 //    fs_type="ext4"   partition_type="EMMC"    location=device
     50 Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
     51     char* result = NULL;
     52     if (argc != 4) {
     53         return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
     54     }
     55     char* fs_type;
     56     char* partition_type;
     57     char* location;
     58     char* mount_point;
     59     if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
     60                  &location, &mount_point) < 0) {
     61         return NULL;
     62     }
     63 
     64     if (strlen(fs_type) == 0) {
     65         ErrorAbort(state, "fs_type argument to %s() can't be empty", name);
     66         goto done;
     67     }
     68     if (strlen(partition_type) == 0) {
     69         ErrorAbort(state, "partition_type argument to %s() can't be empty",
     70                    name);
     71         goto done;
     72     }
     73     if (strlen(location) == 0) {
     74         ErrorAbort(state, "location argument to %s() can't be empty", name);
     75         goto done;
     76     }
     77     if (strlen(mount_point) == 0) {
     78         ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
     79         goto done;
     80     }
     81 
     82     mkdir(mount_point, 0755);
     83 
     84     if (strcmp(partition_type, "MTD") == 0) {
     85         mtd_scan_partitions();
     86         const MtdPartition* mtd;
     87         mtd = mtd_find_partition_by_name(location);
     88         if (mtd == NULL) {
     89             fprintf(stderr, "%s: no mtd partition named \"%s\"",
     90                     name, location);
     91             result = strdup("");
     92             goto done;
     93         }
     94         if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) {
     95             fprintf(stderr, "mtd mount of %s failed: %s\n",
     96                     location, strerror(errno));
     97             result = strdup("");
     98             goto done;
     99         }
    100         result = mount_point;
    101     } else {
    102         if (mount(location, mount_point, fs_type,
    103                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
    104             fprintf(stderr, "%s: failed to mount %s at %s: %s\n",
    105                     name, location, mount_point, strerror(errno));
    106             result = strdup("");
    107         } else {
    108             result = mount_point;
    109         }
    110     }
    111 
    112 done:
    113     free(fs_type);
    114     free(partition_type);
    115     free(location);
    116     if (result != mount_point) free(mount_point);
    117     return StringValue(result);
    118 }
    119 
    120 
    121 // is_mounted(mount_point)
    122 Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
    123     char* result = NULL;
    124     if (argc != 1) {
    125         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
    126     }
    127     char* mount_point;
    128     if (ReadArgs(state, argv, 1, &mount_point) < 0) {
    129         return NULL;
    130     }
    131     if (strlen(mount_point) == 0) {
    132         ErrorAbort(state, "mount_point argument to unmount() can't be empty");
    133         goto done;
    134     }
    135 
    136     scan_mounted_volumes();
    137     const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
    138     if (vol == NULL) {
    139         result = strdup("");
    140     } else {
    141         result = mount_point;
    142     }
    143 
    144 done:
    145     if (result != mount_point) free(mount_point);
    146     return StringValue(result);
    147 }
    148 
    149 
    150 Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
    151     char* result = NULL;
    152     if (argc != 1) {
    153         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
    154     }
    155     char* mount_point;
    156     if (ReadArgs(state, argv, 1, &mount_point) < 0) {
    157         return NULL;
    158     }
    159     if (strlen(mount_point) == 0) {
    160         ErrorAbort(state, "mount_point argument to unmount() can't be empty");
    161         goto done;
    162     }
    163 
    164     scan_mounted_volumes();
    165     const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
    166     if (vol == NULL) {
    167         fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
    168         result = strdup("");
    169     } else {
    170         unmount_mounted_volume(vol);
    171         result = mount_point;
    172     }
    173 
    174 done:
    175     if (result != mount_point) free(mount_point);
    176     return StringValue(result);
    177 }
    178 
    179 
    180 // format(fs_type, partition_type, location, fs_size)
    181 //
    182 //    fs_type="yaffs2" partition_type="MTD"     location=partition fs_size=<bytes>
    183 //    fs_type="ext4"   partition_type="EMMC"    location=device    fs_size=<bytes>
    184 //    if fs_size == 0, then make_ext4fs uses the entire partition.
    185 //    if fs_size > 0, that is the size to use
    186 //    if fs_size < 0, then reserve that many bytes at the end of the partition
    187 Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
    188     char* result = NULL;
    189     if (argc != 4) {
    190         return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
    191     }
    192     char* fs_type;
    193     char* partition_type;
    194     char* location;
    195     char* fs_size;
    196     if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &fs_size) < 0) {
    197         return NULL;
    198     }
    199 
    200     if (strlen(fs_type) == 0) {
    201         ErrorAbort(state, "fs_type argument to %s() can't be empty", name);
    202         goto done;
    203     }
    204     if (strlen(partition_type) == 0) {
    205         ErrorAbort(state, "partition_type argument to %s() can't be empty",
    206                    name);
    207         goto done;
    208     }
    209     if (strlen(location) == 0) {
    210         ErrorAbort(state, "location argument to %s() can't be empty", name);
    211         goto done;
    212     }
    213 
    214     if (strcmp(partition_type, "MTD") == 0) {
    215         mtd_scan_partitions();
    216         const MtdPartition* mtd = mtd_find_partition_by_name(location);
    217         if (mtd == NULL) {
    218             fprintf(stderr, "%s: no mtd partition named \"%s\"",
    219                     name, location);
    220             result = strdup("");
    221             goto done;
    222         }
    223         MtdWriteContext* ctx = mtd_write_partition(mtd);
    224         if (ctx == NULL) {
    225             fprintf(stderr, "%s: can't write \"%s\"", name, location);
    226             result = strdup("");
    227             goto done;
    228         }
    229         if (mtd_erase_blocks(ctx, -1) == -1) {
    230             mtd_write_close(ctx);
    231             fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
    232             result = strdup("");
    233             goto done;
    234         }
    235         if (mtd_write_close(ctx) != 0) {
    236             fprintf(stderr, "%s: failed to close \"%s\"", name, location);
    237             result = strdup("");
    238             goto done;
    239         }
    240         result = location;
    241 #ifdef USE_EXT4
    242     } else if (strcmp(fs_type, "ext4") == 0) {
    243         int status = make_ext4fs(location, atoll(fs_size));
    244         if (status != 0) {
    245             fprintf(stderr, "%s: make_ext4fs failed (%d) on %s",
    246                     name, status, location);
    247             result = strdup("");
    248             goto done;
    249         }
    250         result = location;
    251 #endif
    252     } else {
    253         fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"",
    254                 name, fs_type, partition_type);
    255     }
    256 
    257 done:
    258     free(fs_type);
    259     free(partition_type);
    260     if (result != location) free(location);
    261     return StringValue(result);
    262 }
    263 
    264 
    265 Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
    266     char** paths = malloc(argc * sizeof(char*));
    267     int i;
    268     for (i = 0; i < argc; ++i) {
    269         paths[i] = Evaluate(state, argv[i]);
    270         if (paths[i] == NULL) {
    271             int j;
    272             for (j = 0; j < i; ++i) {
    273                 free(paths[j]);
    274             }
    275             free(paths);
    276             return NULL;
    277         }
    278     }
    279 
    280     bool recursive = (strcmp(name, "delete_recursive") == 0);
    281 
    282     int success = 0;
    283     for (i = 0; i < argc; ++i) {
    284         if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
    285             ++success;
    286         free(paths[i]);
    287     }
    288     free(paths);
    289 
    290     char buffer[10];
    291     sprintf(buffer, "%d", success);
    292     return StringValue(strdup(buffer));
    293 }
    294 
    295 
    296 Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
    297     if (argc != 2) {
    298         return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
    299     }
    300     char* frac_str;
    301     char* sec_str;
    302     if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
    303         return NULL;
    304     }
    305 
    306     double frac = strtod(frac_str, NULL);
    307     int sec = strtol(sec_str, NULL, 10);
    308 
    309     UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    310     fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
    311 
    312     free(sec_str);
    313     return StringValue(frac_str);
    314 }
    315 
    316 Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
    317     if (argc != 1) {
    318         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
    319     }
    320     char* frac_str;
    321     if (ReadArgs(state, argv, 1, &frac_str) < 0) {
    322         return NULL;
    323     }
    324 
    325     double frac = strtod(frac_str, NULL);
    326 
    327     UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    328     fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
    329 
    330     return StringValue(frac_str);
    331 }
    332 
    333 // package_extract_dir(package_path, destination_path)
    334 Value* PackageExtractDirFn(const char* name, State* state,
    335                           int argc, Expr* argv[]) {
    336     if (argc != 2) {
    337         return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
    338     }
    339     char* zip_path;
    340     char* dest_path;
    341     if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
    342 
    343     ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
    344 
    345     // To create a consistent system image, never use the clock for timestamps.
    346     struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
    347 
    348     bool success = mzExtractRecursive(za, zip_path, dest_path,
    349                                       MZ_EXTRACT_FILES_ONLY, &timestamp,
    350                                       NULL, NULL);
    351     free(zip_path);
    352     free(dest_path);
    353     return StringValue(strdup(success ? "t" : ""));
    354 }
    355 
    356 
    357 // package_extract_file(package_path, destination_path)
    358 //   or
    359 // package_extract_file(package_path)
    360 //   to return the entire contents of the file as the result of this
    361 //   function (the char* returned is actually a FileContents*).
    362 Value* PackageExtractFileFn(const char* name, State* state,
    363                            int argc, Expr* argv[]) {
    364     if (argc != 1 && argc != 2) {
    365         return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
    366                           name, argc);
    367     }
    368     bool success = false;
    369     if (argc == 2) {
    370         // The two-argument version extracts to a file.
    371 
    372         char* zip_path;
    373         char* dest_path;
    374         if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
    375 
    376         ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
    377         const ZipEntry* entry = mzFindZipEntry(za, zip_path);
    378         if (entry == NULL) {
    379             fprintf(stderr, "%s: no %s in package\n", name, zip_path);
    380             goto done2;
    381         }
    382 
    383         FILE* f = fopen(dest_path, "wb");
    384         if (f == NULL) {
    385             fprintf(stderr, "%s: can't open %s for write: %s\n",
    386                     name, dest_path, strerror(errno));
    387             goto done2;
    388         }
    389         success = mzExtractZipEntryToFile(za, entry, fileno(f));
    390         fclose(f);
    391 
    392       done2:
    393         free(zip_path);
    394         free(dest_path);
    395         return StringValue(strdup(success ? "t" : ""));
    396     } else {
    397         // The one-argument version returns the contents of the file
    398         // as the result.
    399 
    400         char* zip_path;
    401         Value* v = malloc(sizeof(Value));
    402         v->type = VAL_BLOB;
    403         v->size = -1;
    404         v->data = NULL;
    405 
    406         if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
    407 
    408         ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
    409         const ZipEntry* entry = mzFindZipEntry(za, zip_path);
    410         if (entry == NULL) {
    411             fprintf(stderr, "%s: no %s in package\n", name, zip_path);
    412             goto done1;
    413         }
    414 
    415         v->size = mzGetZipEntryUncompLen(entry);
    416         v->data = malloc(v->size);
    417         if (v->data == NULL) {
    418             fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n",
    419                     name, (long)v->size, zip_path);
    420             goto done1;
    421         }
    422 
    423         success = mzExtractZipEntryToBuffer(za, entry,
    424                                             (unsigned char *)v->data);
    425 
    426       done1:
    427         free(zip_path);
    428         if (!success) {
    429             free(v->data);
    430             v->data = NULL;
    431             v->size = -1;
    432         }
    433         return v;
    434     }
    435 }
    436 
    437 
    438 // retouch_binaries(lib1, lib2, ...)
    439 Value* RetouchBinariesFn(const char* name, State* state,
    440                          int argc, Expr* argv[]) {
    441     UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    442 
    443     char **retouch_entries  = ReadVarArgs(state, argc, argv);
    444     if (retouch_entries == NULL) {
    445         return StringValue(strdup("t"));
    446     }
    447 
    448     // some randomness from the clock
    449     int32_t override_base;
    450     bool override_set = false;
    451     int32_t random_base = time(NULL) % 1024;
    452     // some more randomness from /dev/random
    453     FILE *f_random = fopen("/dev/random", "rb");
    454     uint16_t random_bits = 0;
    455     if (f_random != NULL) {
    456         fread(&random_bits, 2, 1, f_random);
    457         random_bits = random_bits % 1024;
    458         fclose(f_random);
    459     }
    460     random_base = (random_base + random_bits) % 1024;
    461     fprintf(ui->cmd_pipe, "ui_print Random offset: 0x%x\n", random_base);
    462     fprintf(ui->cmd_pipe, "ui_print\n");
    463 
    464     // make sure we never randomize to zero; this let's us look at a file
    465     // and know for sure whether it has been processed; important in the
    466     // crash recovery process
    467     if (random_base == 0) random_base = 1;
    468     // make sure our randomization is page-aligned
    469     random_base *= -0x1000;
    470     override_base = random_base;
    471 
    472     int i = 0;
    473     bool success = true;
    474     while (i < (argc - 1)) {
    475         success = success && retouch_one_library(retouch_entries[i],
    476                                                  retouch_entries[i+1],
    477                                                  random_base,
    478                                                  override_set ?
    479                                                    NULL :
    480                                                    &override_base);
    481         if (!success)
    482             ErrorAbort(state, "Failed to retouch '%s'.", retouch_entries[i]);
    483 
    484         free(retouch_entries[i]);
    485         free(retouch_entries[i+1]);
    486         i += 2;
    487 
    488         if (success && override_base != 0) {
    489             random_base = override_base;
    490             override_set = true;
    491         }
    492     }
    493     if (i < argc) {
    494         free(retouch_entries[i]);
    495         success = false;
    496     }
    497     free(retouch_entries);
    498 
    499     if (!success) {
    500       Value* v = malloc(sizeof(Value));
    501       v->type = VAL_STRING;
    502       v->data = NULL;
    503       v->size = -1;
    504       return v;
    505     }
    506     return StringValue(strdup("t"));
    507 }
    508 
    509 
    510 // undo_retouch_binaries(lib1, lib2, ...)
    511 Value* UndoRetouchBinariesFn(const char* name, State* state,
    512                              int argc, Expr* argv[]) {
    513     UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    514 
    515     char **retouch_entries  = ReadVarArgs(state, argc, argv);
    516     if (retouch_entries == NULL) {
    517         return StringValue(strdup("t"));
    518     }
    519 
    520     int i = 0;
    521     bool success = true;
    522     int32_t override_base;
    523     while (i < (argc-1)) {
    524         success = success && retouch_one_library(retouch_entries[i],
    525                                                  retouch_entries[i+1],
    526                                                  0 /* undo => offset==0 */,
    527                                                  NULL);
    528         if (!success)
    529             ErrorAbort(state, "Failed to unretouch '%s'.",
    530                        retouch_entries[i]);
    531 
    532         free(retouch_entries[i]);
    533         free(retouch_entries[i+1]);
    534         i += 2;
    535     }
    536     if (i < argc) {
    537         free(retouch_entries[i]);
    538         success = false;
    539     }
    540     free(retouch_entries);
    541 
    542     if (!success) {
    543       Value* v = malloc(sizeof(Value));
    544       v->type = VAL_STRING;
    545       v->data = NULL;
    546       v->size = -1;
    547       return v;
    548     }
    549     return StringValue(strdup("t"));
    550 }
    551 
    552 
    553 // symlink target src1 src2 ...
    554 //    unlinks any previously existing src1, src2, etc before creating symlinks.
    555 Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
    556     if (argc == 0) {
    557         return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
    558     }
    559     char* target;
    560     target = Evaluate(state, argv[0]);
    561     if (target == NULL) return NULL;
    562 
    563     char** srcs = ReadVarArgs(state, argc-1, argv+1);
    564     if (srcs == NULL) {
    565         free(target);
    566         return NULL;
    567     }
    568 
    569     int i;
    570     for (i = 0; i < argc-1; ++i) {
    571         if (unlink(srcs[i]) < 0) {
    572             if (errno != ENOENT) {
    573                 fprintf(stderr, "%s: failed to remove %s: %s\n",
    574                         name, srcs[i], strerror(errno));
    575             }
    576         }
    577         if (symlink(target, srcs[i]) < 0) {
    578             fprintf(stderr, "%s: failed to symlink %s to %s: %s\n",
    579                     name, srcs[i], target, strerror(errno));
    580         }
    581         free(srcs[i]);
    582     }
    583     free(srcs);
    584     return StringValue(strdup(""));
    585 }
    586 
    587 
    588 Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
    589     char* result = NULL;
    590     bool recursive = (strcmp(name, "set_perm_recursive") == 0);
    591 
    592     int min_args = 4 + (recursive ? 1 : 0);
    593     if (argc < min_args) {
    594         return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
    595     }
    596 
    597     char** args = ReadVarArgs(state, argc, argv);
    598     if (args == NULL) return NULL;
    599 
    600     char* end;
    601     int i;
    602 
    603     int uid = strtoul(args[0], &end, 0);
    604     if (*end != '\0' || args[0][0] == 0) {
    605         ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
    606         goto done;
    607     }
    608 
    609     int gid = strtoul(args[1], &end, 0);
    610     if (*end != '\0' || args[1][0] == 0) {
    611         ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
    612         goto done;
    613     }
    614 
    615     if (recursive) {
    616         int dir_mode = strtoul(args[2], &end, 0);
    617         if (*end != '\0' || args[2][0] == 0) {
    618             ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
    619             goto done;
    620         }
    621 
    622         int file_mode = strtoul(args[3], &end, 0);
    623         if (*end != '\0' || args[3][0] == 0) {
    624             ErrorAbort(state, "%s: \"%s\" not a valid filemode",
    625                        name, args[3]);
    626             goto done;
    627         }
    628 
    629         for (i = 4; i < argc; ++i) {
    630             dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
    631         }
    632     } else {
    633         int mode = strtoul(args[2], &end, 0);
    634         if (*end != '\0' || args[2][0] == 0) {
    635             ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
    636             goto done;
    637         }
    638 
    639         for (i = 3; i < argc; ++i) {
    640             if (chown(args[i], uid, gid) < 0) {
    641                 fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n",
    642                         name, args[i], uid, gid, strerror(errno));
    643             }
    644             if (chmod(args[i], mode) < 0) {
    645                 fprintf(stderr, "%s: chmod of %s to %o failed: %s\n",
    646                         name, args[i], mode, strerror(errno));
    647             }
    648         }
    649     }
    650     result = strdup("");
    651 
    652 done:
    653     for (i = 0; i < argc; ++i) {
    654         free(args[i]);
    655     }
    656     free(args);
    657 
    658     return StringValue(result);
    659 }
    660 
    661 
    662 Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
    663     if (argc != 1) {
    664         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
    665     }
    666     char* key;
    667     key = Evaluate(state, argv[0]);
    668     if (key == NULL) return NULL;
    669 
    670     char value[PROPERTY_VALUE_MAX];
    671     property_get(key, value, "");
    672     free(key);
    673 
    674     return StringValue(strdup(value));
    675 }
    676 
    677 
    678 // file_getprop(file, key)
    679 //
    680 //   interprets 'file' as a getprop-style file (key=value pairs, one
    681 //   per line, # comment lines and blank lines okay), and returns the value
    682 //   for 'key' (or "" if it isn't defined).
    683 Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
    684     char* result = NULL;
    685     char* buffer = NULL;
    686     char* filename;
    687     char* key;
    688     if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
    689         return NULL;
    690     }
    691 
    692     struct stat st;
    693     if (stat(filename, &st) < 0) {
    694         ErrorAbort(state, "%s: failed to stat \"%s\": %s",
    695                    name, filename, strerror(errno));
    696         goto done;
    697     }
    698 
    699 #define MAX_FILE_GETPROP_SIZE    65536
    700 
    701     if (st.st_size > MAX_FILE_GETPROP_SIZE) {
    702         ErrorAbort(state, "%s too large for %s (max %d)",
    703                    filename, name, MAX_FILE_GETPROP_SIZE);
    704         goto done;
    705     }
    706 
    707     buffer = malloc(st.st_size+1);
    708     if (buffer == NULL) {
    709         ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
    710         goto done;
    711     }
    712 
    713     FILE* f = fopen(filename, "rb");
    714     if (f == NULL) {
    715         ErrorAbort(state, "%s: failed to open %s: %s",
    716                    name, filename, strerror(errno));
    717         goto done;
    718     }
    719 
    720     if (fread(buffer, 1, st.st_size, f) != st.st_size) {
    721         ErrorAbort(state, "%s: failed to read %d bytes from %s",
    722                    name, st.st_size+1, filename);
    723         fclose(f);
    724         goto done;
    725     }
    726     buffer[st.st_size] = '\0';
    727 
    728     fclose(f);
    729 
    730     char* line = strtok(buffer, "\n");
    731     do {
    732         // skip whitespace at start of line
    733         while (*line && isspace(*line)) ++line;
    734 
    735         // comment or blank line: skip to next line
    736         if (*line == '\0' || *line == '#') continue;
    737 
    738         char* equal = strchr(line, '=');
    739         if (equal == NULL) {
    740             ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
    741                        name, line, filename);
    742             goto done;
    743         }
    744 
    745         // trim whitespace between key and '='
    746         char* key_end = equal-1;
    747         while (key_end > line && isspace(*key_end)) --key_end;
    748         key_end[1] = '\0';
    749 
    750         // not the key we're looking for
    751         if (strcmp(key, line) != 0) continue;
    752 
    753         // skip whitespace after the '=' to the start of the value
    754         char* val_start = equal+1;
    755         while(*val_start && isspace(*val_start)) ++val_start;
    756 
    757         // trim trailing whitespace
    758         char* val_end = val_start + strlen(val_start)-1;
    759         while (val_end > val_start && isspace(*val_end)) --val_end;
    760         val_end[1] = '\0';
    761 
    762         result = strdup(val_start);
    763         break;
    764 
    765     } while ((line = strtok(NULL, "\n")));
    766 
    767     if (result == NULL) result = strdup("");
    768 
    769   done:
    770     free(filename);
    771     free(key);
    772     free(buffer);
    773     return StringValue(result);
    774 }
    775 
    776 
    777 static bool write_raw_image_cb(const unsigned char* data,
    778                                int data_len, void* ctx) {
    779     int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
    780     if (r == data_len) return true;
    781     fprintf(stderr, "%s\n", strerror(errno));
    782     return false;
    783 }
    784 
    785 // write_raw_image(filename_or_blob, partition)
    786 Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
    787     char* result = NULL;
    788 
    789     Value* partition_value;
    790     Value* contents;
    791     if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) {
    792         return NULL;
    793     }
    794 
    795     if (partition_value->type != VAL_STRING) {
    796         ErrorAbort(state, "partition argument to %s must be string", name);
    797         goto done;
    798     }
    799     char* partition = partition_value->data;
    800     if (strlen(partition) == 0) {
    801         ErrorAbort(state, "partition argument to %s can't be empty", name);
    802         goto done;
    803     }
    804     if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) {
    805         ErrorAbort(state, "file argument to %s can't be empty", name);
    806         goto done;
    807     }
    808 
    809     mtd_scan_partitions();
    810     const MtdPartition* mtd = mtd_find_partition_by_name(partition);
    811     if (mtd == NULL) {
    812         fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
    813         result = strdup("");
    814         goto done;
    815     }
    816 
    817     MtdWriteContext* ctx = mtd_write_partition(mtd);
    818     if (ctx == NULL) {
    819         fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
    820                 name, partition);
    821         result = strdup("");
    822         goto done;
    823     }
    824 
    825     bool success;
    826 
    827     if (contents->type == VAL_STRING) {
    828         // we're given a filename as the contents
    829         char* filename = contents->data;
    830         FILE* f = fopen(filename, "rb");
    831         if (f == NULL) {
    832             fprintf(stderr, "%s: can't open %s: %s\n",
    833                     name, filename, strerror(errno));
    834             result = strdup("");
    835             goto done;
    836         }
    837 
    838         success = true;
    839         char* buffer = malloc(BUFSIZ);
    840         int read;
    841         while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
    842             int wrote = mtd_write_data(ctx, buffer, read);
    843             success = success && (wrote == read);
    844         }
    845         free(buffer);
    846         fclose(f);
    847     } else {
    848         // we're given a blob as the contents
    849         ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size);
    850         success = (wrote == contents->size);
    851     }
    852     if (!success) {
    853         fprintf(stderr, "mtd_write_data to %s failed: %s\n",
    854                 partition, strerror(errno));
    855     }
    856 
    857     if (mtd_erase_blocks(ctx, -1) == -1) {
    858         fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
    859     }
    860     if (mtd_write_close(ctx) != 0) {
    861         fprintf(stderr, "%s: error closing write of %s\n", name, partition);
    862     }
    863 
    864     printf("%s %s partition\n",
    865            success ? "wrote" : "failed to write", partition);
    866 
    867     result = success ? partition : strdup("");
    868 
    869 done:
    870     if (result != partition) FreeValue(partition_value);
    871     FreeValue(contents);
    872     return StringValue(result);
    873 }
    874 
    875 // apply_patch_space(bytes)
    876 Value* ApplyPatchSpaceFn(const char* name, State* state,
    877                          int argc, Expr* argv[]) {
    878     char* bytes_str;
    879     if (ReadArgs(state, argv, 1, &bytes_str) < 0) {
    880         return NULL;
    881     }
    882 
    883     char* endptr;
    884     size_t bytes = strtol(bytes_str, &endptr, 10);
    885     if (bytes == 0 && endptr == bytes_str) {
    886         ErrorAbort(state, "%s(): can't parse \"%s\" as byte count\n\n",
    887                    name, bytes_str);
    888         free(bytes_str);
    889         return NULL;
    890     }
    891 
    892     return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
    893 }
    894 
    895 
    896 // apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
    897 Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
    898     if (argc < 6 || (argc % 2) == 1) {
    899         return ErrorAbort(state, "%s(): expected at least 6 args and an "
    900                                  "even number, got %d",
    901                           name, argc);
    902     }
    903 
    904     char* source_filename;
    905     char* target_filename;
    906     char* target_sha1;
    907     char* target_size_str;
    908     if (ReadArgs(state, argv, 4, &source_filename, &target_filename,
    909                  &target_sha1, &target_size_str) < 0) {
    910         return NULL;
    911     }
    912 
    913     char* endptr;
    914     size_t target_size = strtol(target_size_str, &endptr, 10);
    915     if (target_size == 0 && endptr == target_size_str) {
    916         ErrorAbort(state, "%s(): can't parse \"%s\" as byte count",
    917                    name, target_size_str);
    918         free(source_filename);
    919         free(target_filename);
    920         free(target_sha1);
    921         free(target_size_str);
    922         return NULL;
    923     }
    924 
    925     int patchcount = (argc-4) / 2;
    926     Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
    927 
    928     int i;
    929     for (i = 0; i < patchcount; ++i) {
    930         if (patches[i*2]->type != VAL_STRING) {
    931             ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
    932             break;
    933         }
    934         if (patches[i*2+1]->type != VAL_BLOB) {
    935             ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
    936             break;
    937         }
    938     }
    939     if (i != patchcount) {
    940         for (i = 0; i < patchcount*2; ++i) {
    941             FreeValue(patches[i]);
    942         }
    943         free(patches);
    944         return NULL;
    945     }
    946 
    947     char** patch_sha_str = malloc(patchcount * sizeof(char*));
    948     for (i = 0; i < patchcount; ++i) {
    949         patch_sha_str[i] = patches[i*2]->data;
    950         patches[i*2]->data = NULL;
    951         FreeValue(patches[i*2]);
    952         patches[i] = patches[i*2+1];
    953     }
    954 
    955     int result = applypatch(source_filename, target_filename,
    956                             target_sha1, target_size,
    957                             patchcount, patch_sha_str, patches);
    958 
    959     for (i = 0; i < patchcount; ++i) {
    960         FreeValue(patches[i]);
    961     }
    962     free(patch_sha_str);
    963     free(patches);
    964 
    965     return StringValue(strdup(result == 0 ? "t" : ""));
    966 }
    967 
    968 // apply_patch_check(file, [sha1_1, ...])
    969 Value* ApplyPatchCheckFn(const char* name, State* state,
    970                          int argc, Expr* argv[]) {
    971     if (argc < 1) {
    972         return ErrorAbort(state, "%s(): expected at least 1 arg, got %d",
    973                           name, argc);
    974     }
    975 
    976     char* filename;
    977     if (ReadArgs(state, argv, 1, &filename) < 0) {
    978         return NULL;
    979     }
    980 
    981     int patchcount = argc-1;
    982     char** sha1s = ReadVarArgs(state, argc-1, argv+1);
    983 
    984     int result = applypatch_check(filename, patchcount, sha1s);
    985 
    986     int i;
    987     for (i = 0; i < patchcount; ++i) {
    988         free(sha1s[i]);
    989     }
    990     free(sha1s);
    991 
    992     return StringValue(strdup(result == 0 ? "t" : ""));
    993 }
    994 
    995 Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
    996     char** args = ReadVarArgs(state, argc, argv);
    997     if (args == NULL) {
    998         return NULL;
    999     }
   1000 
   1001     int size = 0;
   1002     int i;
   1003     for (i = 0; i < argc; ++i) {
   1004         size += strlen(args[i]);
   1005     }
   1006     char* buffer = malloc(size+1);
   1007     size = 0;
   1008     for (i = 0; i < argc; ++i) {
   1009         strcpy(buffer+size, args[i]);
   1010         size += strlen(args[i]);
   1011         free(args[i]);
   1012     }
   1013     free(args);
   1014     buffer[size] = '\0';
   1015 
   1016     char* line = strtok(buffer, "\n");
   1017     while (line) {
   1018         fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
   1019                 "ui_print %s\n", line);
   1020         line = strtok(NULL, "\n");
   1021     }
   1022     fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
   1023 
   1024     return StringValue(buffer);
   1025 }
   1026 
   1027 Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) {
   1028     if (argc != 0) {
   1029         return ErrorAbort(state, "%s() expects no args, got %d", name, argc);
   1030     }
   1031     fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "wipe_cache\n");
   1032     return StringValue(strdup("t"));
   1033 }
   1034 
   1035 Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
   1036     if (argc < 1) {
   1037         return ErrorAbort(state, "%s() expects at least 1 arg", name);
   1038     }
   1039     char** args = ReadVarArgs(state, argc, argv);
   1040     if (args == NULL) {
   1041         return NULL;
   1042     }
   1043 
   1044     char** args2 = malloc(sizeof(char*) * (argc+1));
   1045     memcpy(args2, args, sizeof(char*) * argc);
   1046     args2[argc] = NULL;
   1047 
   1048     fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
   1049 
   1050     pid_t child = fork();
   1051     if (child == 0) {
   1052         execv(args2[0], args2);
   1053         fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
   1054         _exit(1);
   1055     }
   1056     int status;
   1057     waitpid(child, &status, 0);
   1058     if (WIFEXITED(status)) {
   1059         if (WEXITSTATUS(status) != 0) {
   1060             fprintf(stderr, "run_program: child exited with status %d\n",
   1061                     WEXITSTATUS(status));
   1062         }
   1063     } else if (WIFSIGNALED(status)) {
   1064         fprintf(stderr, "run_program: child terminated by signal %d\n",
   1065                 WTERMSIG(status));
   1066     }
   1067 
   1068     int i;
   1069     for (i = 0; i < argc; ++i) {
   1070         free(args[i]);
   1071     }
   1072     free(args);
   1073     free(args2);
   1074 
   1075     char buffer[20];
   1076     sprintf(buffer, "%d", status);
   1077 
   1078     return StringValue(strdup(buffer));
   1079 }
   1080 
   1081 // Take a sha-1 digest and return it as a newly-allocated hex string.
   1082 static char* PrintSha1(uint8_t* digest) {
   1083     char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
   1084     int i;
   1085     const char* alphabet = "0123456789abcdef";
   1086     for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
   1087         buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
   1088         buffer[i*2+1] = alphabet[digest[i] & 0xf];
   1089     }
   1090     buffer[i*2] = '\0';
   1091     return buffer;
   1092 }
   1093 
   1094 // sha1_check(data)
   1095 //    to return the sha1 of the data (given in the format returned by
   1096 //    read_file).
   1097 //
   1098 // sha1_check(data, sha1_hex, [sha1_hex, ...])
   1099 //    returns the sha1 of the file if it matches any of the hex
   1100 //    strings passed, or "" if it does not equal any of them.
   1101 //
   1102 Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
   1103     if (argc < 1) {
   1104         return ErrorAbort(state, "%s() expects at least 1 arg", name);
   1105     }
   1106 
   1107     Value** args = ReadValueVarArgs(state, argc, argv);
   1108     if (args == NULL) {
   1109         return NULL;
   1110     }
   1111 
   1112     if (args[0]->size < 0) {
   1113         fprintf(stderr, "%s(): no file contents received", name);
   1114         return StringValue(strdup(""));
   1115     }
   1116     uint8_t digest[SHA_DIGEST_SIZE];
   1117     SHA(args[0]->data, args[0]->size, digest);
   1118     FreeValue(args[0]);
   1119 
   1120     if (argc == 1) {
   1121         return StringValue(PrintSha1(digest));
   1122     }
   1123 
   1124     int i;
   1125     uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE);
   1126     for (i = 1; i < argc; ++i) {
   1127         if (args[i]->type != VAL_STRING) {
   1128             fprintf(stderr, "%s(): arg %d is not a string; skipping",
   1129                     name, i);
   1130         } else if (ParseSha1(args[i]->data, arg_digest) != 0) {
   1131             // Warn about bad args and skip them.
   1132             fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping",
   1133                     name, args[i]->data);
   1134         } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
   1135             break;
   1136         }
   1137         FreeValue(args[i]);
   1138     }
   1139     if (i >= argc) {
   1140         // Didn't match any of the hex strings; return false.
   1141         return StringValue(strdup(""));
   1142     }
   1143     // Found a match; free all the remaining arguments and return the
   1144     // matched one.
   1145     int j;
   1146     for (j = i+1; j < argc; ++j) {
   1147         FreeValue(args[j]);
   1148     }
   1149     return args[i];
   1150 }
   1151 
   1152 // Read a local file and return its contents (the Value* returned
   1153 // is actually a FileContents*).
   1154 Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
   1155     if (argc != 1) {
   1156         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
   1157     }
   1158     char* filename;
   1159     if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
   1160 
   1161     Value* v = malloc(sizeof(Value));
   1162     v->type = VAL_BLOB;
   1163 
   1164     FileContents fc;
   1165     if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
   1166         ErrorAbort(state, "%s() loading \"%s\" failed: %s",
   1167                    name, filename, strerror(errno));
   1168         free(filename);
   1169         free(v);
   1170         free(fc.data);
   1171         return NULL;
   1172     }
   1173 
   1174     v->size = fc.size;
   1175     v->data = (char*)fc.data;
   1176 
   1177     free(filename);
   1178     return v;
   1179 }
   1180 
   1181 void RegisterInstallFunctions() {
   1182     RegisterFunction("mount", MountFn);
   1183     RegisterFunction("is_mounted", IsMountedFn);
   1184     RegisterFunction("unmount", UnmountFn);
   1185     RegisterFunction("format", FormatFn);
   1186     RegisterFunction("show_progress", ShowProgressFn);
   1187     RegisterFunction("set_progress", SetProgressFn);
   1188     RegisterFunction("delete", DeleteFn);
   1189     RegisterFunction("delete_recursive", DeleteFn);
   1190     RegisterFunction("package_extract_dir", PackageExtractDirFn);
   1191     RegisterFunction("package_extract_file", PackageExtractFileFn);
   1192     RegisterFunction("retouch_binaries", RetouchBinariesFn);
   1193     RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);
   1194     RegisterFunction("symlink", SymlinkFn);
   1195     RegisterFunction("set_perm", SetPermFn);
   1196     RegisterFunction("set_perm_recursive", SetPermFn);
   1197 
   1198     RegisterFunction("getprop", GetPropFn);
   1199     RegisterFunction("file_getprop", FileGetPropFn);
   1200     RegisterFunction("write_raw_image", WriteRawImageFn);
   1201 
   1202     RegisterFunction("apply_patch", ApplyPatchFn);
   1203     RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
   1204     RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
   1205 
   1206     RegisterFunction("read_file", ReadFileFn);
   1207     RegisterFunction("sha1_check", Sha1CheckFn);
   1208 
   1209     RegisterFunction("wipe_cache", WipeCacheFn);
   1210 
   1211     RegisterFunction("ui_print", UIPrintFn);
   1212 
   1213     RegisterFunction("run_program", RunProgramFn);
   1214 }
   1215