blob: 2e965cee599edd2f70ebd783418e8054e43b43db [file] [log] [blame]
Doug Zongker9931f7f2009-06-10 14:11:53 -07001/*
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 <stdio.h>
18#include <errno.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/mount.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <unistd.h>
26
Doug Zongker8edb00c2009-06-11 17:21:44 -070027#include "cutils/misc.h"
28#include "cutils/properties.h"
Doug Zongker9931f7f2009-06-10 14:11:53 -070029#include "edify/expr.h"
30#include "minzip/DirUtil.h"
31#include "mtdutils/mounts.h"
32#include "mtdutils/mtdutils.h"
33#include "updater.h"
34
35char* ErrorAbort(void* cookie, char* format, ...) {
36 char* buffer = malloc(4096);
37 va_list v;
38 va_start(v, format);
39 vsnprintf(buffer, 4096, format, v);
40 va_end(v);
41 SetError(buffer);
42 return NULL;
43}
44
Doug Zongker8edb00c2009-06-11 17:21:44 -070045
Doug Zongker9931f7f2009-06-10 14:11:53 -070046// mount(type, location, mount_point)
47//
48// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
49// type="vfat" location="/dev/block/<whatever>" to mount a device
50char* MountFn(const char* name, void* cookie, int argc, Expr* argv[]) {
51 char* result = NULL;
52 if (argc != 3) {
53 return ErrorAbort(cookie, "%s() expects 3 args, got %d", name, argc);
54 }
55 char* type;
56 char* location;
57 char* mount_point;
58 if (ReadArgs(cookie, argv, 3, &type, &location, &mount_point) < 0) {
59 return NULL;
60 }
61
62 if (strlen(type) == 0) {
63 ErrorAbort(cookie, "type argument to %s() can't be empty", name);
64 goto done;
65 }
66 if (strlen(location) == 0) {
67 ErrorAbort(cookie, "location argument to %s() can't be empty", name);
68 goto done;
69 }
70 if (strlen(mount_point) == 0) {
71 ErrorAbort(cookie, "mount_point argument to %s() can't be empty", name);
72 goto done;
73 }
74
75 mkdir(mount_point, 0755);
76
77 if (strcmp(type, "MTD") == 0) {
78 mtd_scan_partitions();
79 const MtdPartition* mtd;
80 mtd = mtd_find_partition_by_name(location);
81 if (mtd == NULL) {
82 fprintf(stderr, "%s: no mtd partition named \"%s\"",
83 name, location);
84 result = strdup("");
85 goto done;
86 }
87 if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
88 fprintf(stderr, "mtd mount of %s failed: %s\n",
89 location, strerror(errno));
90 result = strdup("");
91 goto done;
92 }
93 result = mount_point;
94 } else {
95 if (mount(location, mount_point, type,
96 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
97 result = strdup("");
98 } else {
99 result = mount_point;
100 }
101 }
102
103done:
104 free(type);
105 free(location);
106 if (result != mount_point) free(mount_point);
107 return result;
108}
109
Doug Zongker8edb00c2009-06-11 17:21:44 -0700110
111// is_mounted(mount_point)
112char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) {
113 char* result = NULL;
114 if (argc != 1) {
115 return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc);
116 }
117 char* mount_point;
118 if (ReadArgs(cookie, argv, 1, &mount_point) < 0) {
119 return NULL;
120 }
121 if (strlen(mount_point) == 0) {
122 ErrorAbort(cookie, "mount_point argument to unmount() can't be empty");
123 goto done;
124 }
125
126 scan_mounted_volumes();
127 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
128 if (vol == NULL) {
129 result = strdup("");
130 } else {
131 result = mount_point;
132 }
133
134done:
135 if (result != mount_point) free(mount_point);
136 return result;
137}
138
139
Doug Zongker9931f7f2009-06-10 14:11:53 -0700140char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) {
141 char* result = NULL;
142 if (argc != 1) {
143 return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc);
144 }
145 char* mount_point;
146 if (ReadArgs(cookie, argv, 1, &mount_point) < 0) {
147 return NULL;
148 }
149 if (strlen(mount_point) == 0) {
150 ErrorAbort(cookie, "mount_point argument to unmount() can't be empty");
151 goto done;
152 }
153
154 scan_mounted_volumes();
155 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
156 if (vol == NULL) {
157 fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
158 result = strdup("");
159 } else {
160 unmount_mounted_volume(vol);
161 result = mount_point;
162 }
163
164done:
165 if (result != mount_point) free(mount_point);
166 return result;
167}
Doug Zongker8edb00c2009-06-11 17:21:44 -0700168
169
Doug Zongker9931f7f2009-06-10 14:11:53 -0700170// format(type, location)
171//
172// type="MTD" location=partition
173char* FormatFn(const char* name, void* cookie, int argc, Expr* argv[]) {
174 char* result = NULL;
175 if (argc != 2) {
176 return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
177 }
178 char* type;
179 char* location;
180 if (ReadArgs(cookie, argv, 2, &type, &location) < 0) {
181 return NULL;
182 }
183
184 if (strlen(type) == 0) {
185 ErrorAbort(cookie, "type argument to %s() can't be empty", name);
186 goto done;
187 }
188 if (strlen(location) == 0) {
189 ErrorAbort(cookie, "location argument to %s() can't be empty", name);
190 goto done;
191 }
192
193 if (strcmp(type, "MTD") == 0) {
194 mtd_scan_partitions();
195 const MtdPartition* mtd = mtd_find_partition_by_name(location);
196 if (mtd == NULL) {
197 fprintf(stderr, "%s: no mtd partition named \"%s\"",
198 name, location);
199 result = strdup("");
200 goto done;
201 }
202 MtdWriteContext* ctx = mtd_write_partition(mtd);
203 if (ctx == NULL) {
204 fprintf(stderr, "%s: can't write \"%s\"", name, location);
205 result = strdup("");
206 goto done;
207 }
208 if (mtd_erase_blocks(ctx, -1) == -1) {
209 mtd_write_close(ctx);
210 fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
211 result = strdup("");
212 goto done;
213 }
214 if (mtd_write_close(ctx) != 0) {
215 fprintf(stderr, "%s: failed to close \"%s\"", name, location);
216 result = strdup("");
217 goto done;
218 }
219 result = location;
220 } else {
221 fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
222 }
223
224done:
225 free(type);
226 if (result != location) free(location);
227 return result;
228}
229
Doug Zongker8edb00c2009-06-11 17:21:44 -0700230
Doug Zongker9931f7f2009-06-10 14:11:53 -0700231char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) {
232 char** paths = malloc(argc * sizeof(char*));
233 int i;
234 for (i = 0; i < argc; ++i) {
235 paths[i] = Evaluate(cookie, argv[i]);
236 if (paths[i] == NULL) {
237 int j;
238 for (j = 0; j < i; ++i) {
239 free(paths[j]);
240 }
241 free(paths);
242 return NULL;
243 }
244 }
245
246 bool recursive = (strcmp(name, "delete_recursive") == 0);
247
248 int success = 0;
249 for (i = 0; i < argc; ++i) {
250 if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
251 ++success;
252 free(paths[i]);
253 }
254 free(paths);
255
256 char buffer[10];
257 sprintf(buffer, "%d", success);
258 return strdup(buffer);
259}
260
Doug Zongker8edb00c2009-06-11 17:21:44 -0700261
Doug Zongker9931f7f2009-06-10 14:11:53 -0700262char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) {
263 if (argc != 2) {
264 return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
265 }
266 char* frac_str;
267 char* sec_str;
268 if (ReadArgs(cookie, argv, 2, &frac_str, &sec_str) < 0) {
269 return NULL;
270 }
271
272 double frac = strtod(frac_str, NULL);
273 int sec = strtol(sec_str, NULL, 10);
274
275 UpdaterInfo* ui = (UpdaterInfo*)cookie;
276 fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
277
278 free(frac_str);
279 free(sec_str);
280 return strdup("");
281}
282
Doug Zongker8edb00c2009-06-11 17:21:44 -0700283// package_extract_dir(package_path, destination_path)
284char* PackageExtractDirFn(const char* name, void* cookie,
285 int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700286 if (argc != 2) {
287 return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
288 }
289 char* zip_path;
290 char* dest_path;
291 if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL;
292
293 ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip;
294
295 // To create a consistent system image, never use the clock for timestamps.
296 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
297
298 bool success = mzExtractRecursive(za, zip_path, dest_path,
299 MZ_EXTRACT_FILES_ONLY, &timestamp,
300 NULL, NULL);
301 free(zip_path);
302 free(dest_path);
303 return strdup(success ? "t" : "");
304}
305
Doug Zongker8edb00c2009-06-11 17:21:44 -0700306
307// package_extract_file(package_path, destination_path)
308char* PackageExtractFileFn(const char* name, void* cookie,
309 int argc, Expr* argv[]) {
310 if (argc != 2) {
311 return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
312 }
313 char* zip_path;
314 char* dest_path;
315 if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL;
316
317 bool success = false;
318
319 ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip;
320 const ZipEntry* entry = mzFindZipEntry(za, zip_path);
321 if (entry == NULL) {
322 fprintf(stderr, "%s: no %s in package\n", name, zip_path);
323 goto done;
324 }
325
326 FILE* f = fopen(dest_path, "wb");
327 if (f == NULL) {
328 fprintf(stderr, "%s: can't open %s for write: %s\n",
329 name, dest_path, strerror(errno));
330 goto done;
331 }
332 success = mzExtractZipEntryToFile(za, entry, fileno(f));
333 fclose(f);
334
335 done:
336 free(zip_path);
337 free(dest_path);
338 return strdup(success ? "t" : "");
339}
340
341
Doug Zongker9931f7f2009-06-10 14:11:53 -0700342// symlink target src1 src2 ...
343char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) {
344 if (argc == 0) {
345 return ErrorAbort(cookie, "%s() expects 1+ args, got %d", name, argc);
346 }
347 char* target;
348 target = Evaluate(cookie, argv[0]);
349 if (target == NULL) return NULL;
350
351 char** srcs = ReadVarArgs(cookie, argc-1, argv+1);
352 if (srcs == NULL) {
353 free(target);
354 return NULL;
355 }
356
357 int i;
358 for (i = 0; i < argc-1; ++i) {
359 symlink(target, srcs[i]);
360 free(srcs[i]);
361 }
362 free(srcs);
363 return strdup("");
364}
365
Doug Zongker8edb00c2009-06-11 17:21:44 -0700366
Doug Zongker9931f7f2009-06-10 14:11:53 -0700367char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) {
368 char* result = NULL;
369 bool recursive = (strcmp(name, "set_perm_recursive") == 0);
370
371 int min_args = 4 + (recursive ? 1 : 0);
372 if (argc < min_args) {
373 return ErrorAbort(cookie, "%s() expects %d+ args, got %d", name, argc);
374 }
375
376 char** args = ReadVarArgs(cookie, argc, argv);
377 if (args == NULL) return NULL;
378
379 char* end;
380 int i;
381
382 int uid = strtoul(args[0], &end, 0);
383 if (*end != '\0' || args[0][0] == 0) {
384 ErrorAbort(cookie, "%s: \"%s\" not a valid uid", name, args[0]);
385 goto done;
386 }
387
388 int gid = strtoul(args[1], &end, 0);
389 if (*end != '\0' || args[1][0] == 0) {
390 ErrorAbort(cookie, "%s: \"%s\" not a valid gid", name, args[1]);
391 goto done;
392 }
393
394 if (recursive) {
395 int dir_mode = strtoul(args[2], &end, 0);
396 if (*end != '\0' || args[2][0] == 0) {
397 ErrorAbort(cookie, "%s: \"%s\" not a valid dirmode", name, args[2]);
398 goto done;
399 }
400
401 int file_mode = strtoul(args[3], &end, 0);
402 if (*end != '\0' || args[3][0] == 0) {
403 ErrorAbort(cookie, "%s: \"%s\" not a valid filemode",
404 name, args[3]);
405 goto done;
406 }
407
408 for (i = 4; i < argc; ++i) {
409 dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
410 }
411 } else {
412 int mode = strtoul(args[2], &end, 0);
413 if (*end != '\0' || args[2][0] == 0) {
414 ErrorAbort(cookie, "%s: \"%s\" not a valid mode", name, args[2]);
415 goto done;
416 }
417
418 for (i = 4; i < argc; ++i) {
419 chown(args[i], uid, gid);
420 chmod(args[i], mode);
421 }
422 }
423 result = strdup("");
424
425done:
426 for (i = 0; i < argc; ++i) {
427 free(args[i]);
428 }
429 free(args);
430
431 return result;
432}
433
Doug Zongker8edb00c2009-06-11 17:21:44 -0700434
435char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) {
436 if (argc != 1) {
437 return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc);
438 }
439 char* key;
440 key = Evaluate(cookie, argv[0]);
441 if (key == NULL) return NULL;
442
443 char value[PROPERTY_VALUE_MAX];
444 property_get(key, value, "");
445 free(key);
446
447 return strdup(value);
448}
449
450
451static bool write_raw_image_cb(const unsigned char* data,
452 int data_len, void* ctx) {
453 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
454 if (r == data_len) return true;
455 fprintf(stderr, "%s\n", strerror(errno));
456 return false;
457}
458
459// write_raw_image(file, partition)
460char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) {
461 char* result = NULL;
462
463 char* partition;
464 char* filename;
465 if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) {
466 return NULL;
467 }
468
469 if (strlen(partition) == 0) {
470 ErrorAbort(cookie, "partition argument to %s can't be empty", name);
471 goto done;
472 }
473 if (strlen(filename) == 0) {
474 ErrorAbort(cookie, "file argument to %s can't be empty", name);
475 goto done;
476 }
477
478 mtd_scan_partitions();
479 const MtdPartition* mtd = mtd_find_partition_by_name(partition);
480 if (mtd == NULL) {
481 fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
482 result = strdup("");
483 goto done;
484 }
485
486 MtdWriteContext* ctx = mtd_write_partition(mtd);
487 if (ctx == NULL) {
488 fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
489 name, partition);
490 result = strdup("");
491 goto done;
492 }
493
494 bool success;
495
496 FILE* f = fopen(filename, "rb");
497 if (f == NULL) {
498 fprintf(stderr, "%s: can't open %s: %s\n",
499 name, filename, strerror(errno));
500 result = strdup("");
501 goto done;
502 }
503
504 success = true;
505 char* buffer = malloc(BUFSIZ);
506 int read;
507 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
508 int wrote = mtd_write_data(ctx, buffer, read);
509 success = success && (wrote == read);
510 if (!success) {
511 fprintf(stderr, "mtd_write_data to %s failed: %s\n",
512 partition, strerror(errno));
513 }
514 }
515 free(buffer);
516 fclose(f);
517
518 printf("%s %s partition from %s\n",
519 success ? "wrote" : "failed to write", partition, filename);
520
521 result = success ? partition : strdup("");
522
523done:
524 if (result != partition) free(partition);
525 free(filename);
526 return result;
527}
528
529// write_firmware_image(file, partition)
530//
531// partition is "radio" or "hboot"
532// file is not used until after updater exits
533//
534// TODO: this should live in some HTC-specific library
535char* WriteFirmwareImageFn(const char* name, void* cookie,
536 int argc, Expr* argv[]) {
537 char* result = NULL;
538
539 char* partition;
540 char* filename;
541 if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) {
542 return NULL;
543 }
544
545 if (strlen(partition) == 0) {
546 ErrorAbort(cookie, "partition argument to %s can't be empty", name);
547 goto done;
548 }
549 if (strlen(filename) == 0) {
550 ErrorAbort(cookie, "file argument to %s can't be empty", name);
551 goto done;
552 }
553
554 FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe;
555 fprintf(cmd, "firmware %s %s\n", partition, filename);
556
557 printf("will write %s firmware from %s\n", partition, filename);
558 result = partition;
559
560done:
561 if (result != partition) free(partition);
562 free(filename);
563 return result;
564}
565
566
567extern int applypatch(int argc, char** argv);
568
569// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
570// apply_patch_check(file, sha1, ...)
571// apply_patch_space(bytes)
572char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) {
573 printf("in applypatchfn (%s)\n", name);
574
575 char* prepend = NULL;
576 if (strstr(name, "check") != NULL) {
577 prepend = "-c";
578 } else if (strstr(name, "space") != NULL) {
579 prepend = "-s";
580 }
581
582 char** args = ReadVarArgs(cookie, argc, argv);
583 if (args == NULL) return NULL;
584
585 // insert the "program name" argv[0] and a copy of the "prepend"
586 // string (if any) at the start of the args.
587
588 int extra = 1 + (prepend != NULL ? 1 : 0);
589 char** temp = malloc((argc+extra) * sizeof(char*));
590 memcpy(temp+extra, args, argc * sizeof(char*));
591 temp[0] = strdup("updater");
592 if (prepend) {
593 temp[1] = strdup(prepend);
594 }
595 free(args);
596 args = temp;
597 argc += extra;
598
599 printf("calling applypatch\n");
600 fflush(stdout);
601 int result = applypatch(argc, args);
602 printf("applypatch returned %d\n", result);
603
604 int i;
605 for (i = 0; i < argc; ++i) {
606 free(args[i]);
607 }
608 free(args);
609
610 switch (result) {
611 case 0: return strdup("t");
612 case 1: return strdup("");
613 default: return ErrorAbort(cookie, "applypatch couldn't parse args");
614 }
615}
616
617
Doug Zongker9931f7f2009-06-10 14:11:53 -0700618void RegisterInstallFunctions() {
619 RegisterFunction("mount", MountFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700620 RegisterFunction("is_mounted", IsMountedFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700621 RegisterFunction("unmount", UnmountFn);
622 RegisterFunction("format", FormatFn);
623 RegisterFunction("show_progress", ShowProgressFn);
624 RegisterFunction("delete", DeleteFn);
625 RegisterFunction("delete_recursive", DeleteFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700626 RegisterFunction("package_extract_dir", PackageExtractDirFn);
627 RegisterFunction("package_extract_file", PackageExtractFileFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700628 RegisterFunction("symlink", SymlinkFn);
629 RegisterFunction("set_perm", SetPermFn);
630 RegisterFunction("set_perm_recursive", SetPermFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700631
632 RegisterFunction("getprop", GetPropFn);
633 RegisterFunction("write_raw_image", WriteRawImageFn);
634 RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
635
636 RegisterFunction("apply_patch", ApplyPatchFn);
637 RegisterFunction("apply_patch_check", ApplyPatchFn);
638 RegisterFunction("apply_patch_space", ApplyPatchFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700639}