install.c revision daefc1d442fb421606680feb9aeb59c133f4c427
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
50Value* 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
112done:
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)
122Value* 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
144done:
145    if (result != mount_point) free(mount_point);
146    return StringValue(result);
147}
148
149
150Value* 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
174done:
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
187Value* 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
257done:
258    free(fs_type);
259    free(partition_type);
260    if (result != location) free(location);
261    return StringValue(result);
262}
263
264
265Value* 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
296Value* 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
316Value* 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)
334Value* 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*).
362Value* 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, ...)
439Value* 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, ...)
511Value* 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.
555Value* 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
588Value* 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
652done:
653    for (i = 0; i < argc; ++i) {
654        free(args[i]);
655    }
656    free(args);
657
658    return StringValue(result);
659}
660
661
662Value* 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).
683Value* 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
777static 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)
786Value* 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    char* partition = NULL;
796    if (partition_value->type != VAL_STRING) {
797        ErrorAbort(state, "partition argument to %s must be string", name);
798        goto done;
799    }
800    partition = partition_value->data;
801    if (strlen(partition) == 0) {
802        ErrorAbort(state, "partition argument to %s can't be empty", name);
803        goto done;
804    }
805    if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) {
806        ErrorAbort(state, "file argument to %s can't be empty", name);
807        goto done;
808    }
809
810    mtd_scan_partitions();
811    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
812    if (mtd == NULL) {
813        fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
814        result = strdup("");
815        goto done;
816    }
817
818    MtdWriteContext* ctx = mtd_write_partition(mtd);
819    if (ctx == NULL) {
820        fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
821                name, partition);
822        result = strdup("");
823        goto done;
824    }
825
826    bool success;
827
828    if (contents->type == VAL_STRING) {
829        // we're given a filename as the contents
830        char* filename = contents->data;
831        FILE* f = fopen(filename, "rb");
832        if (f == NULL) {
833            fprintf(stderr, "%s: can't open %s: %s\n",
834                    name, filename, strerror(errno));
835            result = strdup("");
836            goto done;
837        }
838
839        success = true;
840        char* buffer = malloc(BUFSIZ);
841        int read;
842        while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
843            int wrote = mtd_write_data(ctx, buffer, read);
844            success = success && (wrote == read);
845        }
846        free(buffer);
847        fclose(f);
848    } else {
849        // we're given a blob as the contents
850        ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size);
851        success = (wrote == contents->size);
852    }
853    if (!success) {
854        fprintf(stderr, "mtd_write_data to %s failed: %s\n",
855                partition, strerror(errno));
856    }
857
858    if (mtd_erase_blocks(ctx, -1) == -1) {
859        fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
860    }
861    if (mtd_write_close(ctx) != 0) {
862        fprintf(stderr, "%s: error closing write of %s\n", name, partition);
863    }
864
865    printf("%s %s partition\n",
866           success ? "wrote" : "failed to write", partition);
867
868    result = success ? partition : strdup("");
869
870done:
871    if (result != partition) FreeValue(partition_value);
872    FreeValue(contents);
873    return StringValue(result);
874}
875
876// apply_patch_space(bytes)
877Value* ApplyPatchSpaceFn(const char* name, State* state,
878                         int argc, Expr* argv[]) {
879    char* bytes_str;
880    if (ReadArgs(state, argv, 1, &bytes_str) < 0) {
881        return NULL;
882    }
883
884    char* endptr;
885    size_t bytes = strtol(bytes_str, &endptr, 10);
886    if (bytes == 0 && endptr == bytes_str) {
887        ErrorAbort(state, "%s(): can't parse \"%s\" as byte count\n\n",
888                   name, bytes_str);
889        free(bytes_str);
890        return NULL;
891    }
892
893    return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
894}
895
896
897// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
898Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
899    if (argc < 6 || (argc % 2) == 1) {
900        return ErrorAbort(state, "%s(): expected at least 6 args and an "
901                                 "even number, got %d",
902                          name, argc);
903    }
904
905    char* source_filename;
906    char* target_filename;
907    char* target_sha1;
908    char* target_size_str;
909    if (ReadArgs(state, argv, 4, &source_filename, &target_filename,
910                 &target_sha1, &target_size_str) < 0) {
911        return NULL;
912    }
913
914    char* endptr;
915    size_t target_size = strtol(target_size_str, &endptr, 10);
916    if (target_size == 0 && endptr == target_size_str) {
917        ErrorAbort(state, "%s(): can't parse \"%s\" as byte count",
918                   name, target_size_str);
919        free(source_filename);
920        free(target_filename);
921        free(target_sha1);
922        free(target_size_str);
923        return NULL;
924    }
925
926    int patchcount = (argc-4) / 2;
927    Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
928
929    int i;
930    for (i = 0; i < patchcount; ++i) {
931        if (patches[i*2]->type != VAL_STRING) {
932            ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
933            break;
934        }
935        if (patches[i*2+1]->type != VAL_BLOB) {
936            ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
937            break;
938        }
939    }
940    if (i != patchcount) {
941        for (i = 0; i < patchcount*2; ++i) {
942            FreeValue(patches[i]);
943        }
944        free(patches);
945        return NULL;
946    }
947
948    char** patch_sha_str = malloc(patchcount * sizeof(char*));
949    for (i = 0; i < patchcount; ++i) {
950        patch_sha_str[i] = patches[i*2]->data;
951        patches[i*2]->data = NULL;
952        FreeValue(patches[i*2]);
953        patches[i] = patches[i*2+1];
954    }
955
956    int result = applypatch(source_filename, target_filename,
957                            target_sha1, target_size,
958                            patchcount, patch_sha_str, patches);
959
960    for (i = 0; i < patchcount; ++i) {
961        FreeValue(patches[i]);
962    }
963    free(patch_sha_str);
964    free(patches);
965
966    return StringValue(strdup(result == 0 ? "t" : ""));
967}
968
969// apply_patch_check(file, [sha1_1, ...])
970Value* ApplyPatchCheckFn(const char* name, State* state,
971                         int argc, Expr* argv[]) {
972    if (argc < 1) {
973        return ErrorAbort(state, "%s(): expected at least 1 arg, got %d",
974                          name, argc);
975    }
976
977    char* filename;
978    if (ReadArgs(state, argv, 1, &filename) < 0) {
979        return NULL;
980    }
981
982    int patchcount = argc-1;
983    char** sha1s = ReadVarArgs(state, argc-1, argv+1);
984
985    int result = applypatch_check(filename, patchcount, sha1s);
986
987    int i;
988    for (i = 0; i < patchcount; ++i) {
989        free(sha1s[i]);
990    }
991    free(sha1s);
992
993    return StringValue(strdup(result == 0 ? "t" : ""));
994}
995
996Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
997    char** args = ReadVarArgs(state, argc, argv);
998    if (args == NULL) {
999        return NULL;
1000    }
1001
1002    int size = 0;
1003    int i;
1004    for (i = 0; i < argc; ++i) {
1005        size += strlen(args[i]);
1006    }
1007    char* buffer = malloc(size+1);
1008    size = 0;
1009    for (i = 0; i < argc; ++i) {
1010        strcpy(buffer+size, args[i]);
1011        size += strlen(args[i]);
1012        free(args[i]);
1013    }
1014    free(args);
1015    buffer[size] = '\0';
1016
1017    char* line = strtok(buffer, "\n");
1018    while (line) {
1019        fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
1020                "ui_print %s\n", line);
1021        line = strtok(NULL, "\n");
1022    }
1023    fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
1024
1025    return StringValue(buffer);
1026}
1027
1028Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) {
1029    if (argc != 0) {
1030        return ErrorAbort(state, "%s() expects no args, got %d", name, argc);
1031    }
1032    fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "wipe_cache\n");
1033    return StringValue(strdup("t"));
1034}
1035
1036Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
1037    if (argc < 1) {
1038        return ErrorAbort(state, "%s() expects at least 1 arg", name);
1039    }
1040    char** args = ReadVarArgs(state, argc, argv);
1041    if (args == NULL) {
1042        return NULL;
1043    }
1044
1045    char** args2 = malloc(sizeof(char*) * (argc+1));
1046    memcpy(args2, args, sizeof(char*) * argc);
1047    args2[argc] = NULL;
1048
1049    fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
1050
1051    pid_t child = fork();
1052    if (child == 0) {
1053        execv(args2[0], args2);
1054        fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
1055        _exit(1);
1056    }
1057    int status;
1058    waitpid(child, &status, 0);
1059    if (WIFEXITED(status)) {
1060        if (WEXITSTATUS(status) != 0) {
1061            fprintf(stderr, "run_program: child exited with status %d\n",
1062                    WEXITSTATUS(status));
1063        }
1064    } else if (WIFSIGNALED(status)) {
1065        fprintf(stderr, "run_program: child terminated by signal %d\n",
1066                WTERMSIG(status));
1067    }
1068
1069    int i;
1070    for (i = 0; i < argc; ++i) {
1071        free(args[i]);
1072    }
1073    free(args);
1074    free(args2);
1075
1076    char buffer[20];
1077    sprintf(buffer, "%d", status);
1078
1079    return StringValue(strdup(buffer));
1080}
1081
1082// Take a sha-1 digest and return it as a newly-allocated hex string.
1083static char* PrintSha1(uint8_t* digest) {
1084    char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
1085    int i;
1086    const char* alphabet = "0123456789abcdef";
1087    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
1088        buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
1089        buffer[i*2+1] = alphabet[digest[i] & 0xf];
1090    }
1091    buffer[i*2] = '\0';
1092    return buffer;
1093}
1094
1095// sha1_check(data)
1096//    to return the sha1 of the data (given in the format returned by
1097//    read_file).
1098//
1099// sha1_check(data, sha1_hex, [sha1_hex, ...])
1100//    returns the sha1 of the file if it matches any of the hex
1101//    strings passed, or "" if it does not equal any of them.
1102//
1103Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
1104    if (argc < 1) {
1105        return ErrorAbort(state, "%s() expects at least 1 arg", name);
1106    }
1107
1108    Value** args = ReadValueVarArgs(state, argc, argv);
1109    if (args == NULL) {
1110        return NULL;
1111    }
1112
1113    if (args[0]->size < 0) {
1114        fprintf(stderr, "%s(): no file contents received", name);
1115        return StringValue(strdup(""));
1116    }
1117    uint8_t digest[SHA_DIGEST_SIZE];
1118    SHA(args[0]->data, args[0]->size, digest);
1119    FreeValue(args[0]);
1120
1121    if (argc == 1) {
1122        return StringValue(PrintSha1(digest));
1123    }
1124
1125    int i;
1126    uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE);
1127    for (i = 1; i < argc; ++i) {
1128        if (args[i]->type != VAL_STRING) {
1129            fprintf(stderr, "%s(): arg %d is not a string; skipping",
1130                    name, i);
1131        } else if (ParseSha1(args[i]->data, arg_digest) != 0) {
1132            // Warn about bad args and skip them.
1133            fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping",
1134                    name, args[i]->data);
1135        } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
1136            break;
1137        }
1138        FreeValue(args[i]);
1139    }
1140    if (i >= argc) {
1141        // Didn't match any of the hex strings; return false.
1142        return StringValue(strdup(""));
1143    }
1144    // Found a match; free all the remaining arguments and return the
1145    // matched one.
1146    int j;
1147    for (j = i+1; j < argc; ++j) {
1148        FreeValue(args[j]);
1149    }
1150    return args[i];
1151}
1152
1153// Read a local file and return its contents (the Value* returned
1154// is actually a FileContents*).
1155Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
1156    if (argc != 1) {
1157        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
1158    }
1159    char* filename;
1160    if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
1161
1162    Value* v = malloc(sizeof(Value));
1163    v->type = VAL_BLOB;
1164
1165    FileContents fc;
1166    if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
1167        ErrorAbort(state, "%s() loading \"%s\" failed: %s",
1168                   name, filename, strerror(errno));
1169        free(filename);
1170        free(v);
1171        free(fc.data);
1172        return NULL;
1173    }
1174
1175    v->size = fc.size;
1176    v->data = (char*)fc.data;
1177
1178    free(filename);
1179    return v;
1180}
1181
1182void RegisterInstallFunctions() {
1183    RegisterFunction("mount", MountFn);
1184    RegisterFunction("is_mounted", IsMountedFn);
1185    RegisterFunction("unmount", UnmountFn);
1186    RegisterFunction("format", FormatFn);
1187    RegisterFunction("show_progress", ShowProgressFn);
1188    RegisterFunction("set_progress", SetProgressFn);
1189    RegisterFunction("delete", DeleteFn);
1190    RegisterFunction("delete_recursive", DeleteFn);
1191    RegisterFunction("package_extract_dir", PackageExtractDirFn);
1192    RegisterFunction("package_extract_file", PackageExtractFileFn);
1193    RegisterFunction("retouch_binaries", RetouchBinariesFn);
1194    RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);
1195    RegisterFunction("symlink", SymlinkFn);
1196    RegisterFunction("set_perm", SetPermFn);
1197    RegisterFunction("set_perm_recursive", SetPermFn);
1198
1199    RegisterFunction("getprop", GetPropFn);
1200    RegisterFunction("file_getprop", FileGetPropFn);
1201    RegisterFunction("write_raw_image", WriteRawImageFn);
1202
1203    RegisterFunction("apply_patch", ApplyPatchFn);
1204    RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
1205    RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
1206
1207    RegisterFunction("read_file", ReadFileFn);
1208    RegisterFunction("sha1_check", Sha1CheckFn);
1209
1210    RegisterFunction("wipe_cache", WipeCacheFn);
1211
1212    RegisterFunction("ui_print", UIPrintFn);
1213
1214    RegisterFunction("run_program", RunProgramFn);
1215}
1216