blob: 7617a8d81978144a993655d4b33e236753402119 [file] [log] [blame]
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -08001/*
2 * Copyright (C) 2007 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#undef NDEBUG
18
19#include <assert.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <unistd.h>
30#include <unistd.h>
31
32#include "amend/commands.h"
33#include "commands.h"
34#include "common.h"
35#include "cutils/misc.h"
36#include "cutils/properties.h"
37#include "firmware.h"
38#include "minzip/DirUtil.h"
39#include "minzip/Zip.h"
40#include "roots.h"
41
42#include "extendedcommands.h"
43
44static int gDidShowProgress = 0;
45
46#define UNUSED(p) ((void)(p))
47
48#define CHECK_BOOL() \
49 do { \
50 assert(argv == NULL); \
51 if (argv != NULL) return -1; \
52 assert(argc == true || argc == false); \
53 if (argc != true && argc != false) return -1; \
54 } while (false)
55
56#define CHECK_WORDS() \
57 do { \
58 assert(argc >= 0); \
59 if (argc < 0) return -1; \
60 assert(argc == 0 || argv != NULL); \
61 if (argc != 0 && argv == NULL) return -1; \
62 if (permissions != NULL) { \
63 int CW_I_; \
64 for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
65 assert(argv[CW_I_] != NULL); \
66 if (argv[CW_I_] == NULL) return -1; \
67 } \
68 } \
69 } while (false)
70
71#define CHECK_FN() \
72 do { \
73 CHECK_WORDS(); \
74 if (permissions != NULL) { \
75 assert(result == NULL); \
76 if (result != NULL) return -1; \
77 } else { \
78 assert(result != NULL); \
79 if (result == NULL) return -1; \
80 } \
81 } while (false)
82
83#define NO_PERMS(perms) \
84 do { \
85 PermissionRequestList *NP_PRL_ = (perms); \
86 if (NP_PRL_ != NULL) { \
87 int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
88 "", false, PERM_NONE); \
89 if (NP_RET_ < 0) { \
90 /* Returns from the calling function. \
91 */ \
92 return NP_RET_; \
93 } \
94 } \
95 } while (false)
96
97/*
98 * Command definitions
99 */
100
101/* assert <boolexpr>
102 */
103static int
104cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
105 PermissionRequestList *permissions)
106{
107 UNUSED(name);
108 UNUSED(cookie);
109 CHECK_BOOL();
110 NO_PERMS(permissions);
111
112 if (!script_assert_enabled) {
113 return 0;
114 }
115
116 /* If our argument is false, return non-zero (failure)
117 * If our argument is true, return zero (success)
118 */
119 if (argc) {
120 return 0;
121 } else {
122 return 1;
123 }
124}
125
126/* format <root>
127 */
128static int
129cmd_format(const char *name, void *cookie, int argc, const char *argv[],
130 PermissionRequestList *permissions)
131{
132 UNUSED(name);
133 UNUSED(cookie);
134 CHECK_WORDS();
135
136 if (argc != 1) {
137 LOGE("Command %s requires exactly one argument\n", name);
138 return 1;
139 }
140 const char *root = argv[0];
141 ui_print("Formatting %s...\n", root);
142
143 int ret = format_root_device(root);
144 if (ret != 0) {
145 LOGE("Can't format %s\n", root);
146 return 1;
147 }
148
149 return 0;
150}
151
152/* delete <file1> [<fileN> ...]
153 * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
154 *
155 * Like "rm -f", will try to delete every named file/dir, even if
156 * earlier ones fail. Recursive deletes that fail halfway through
157 * give up early.
158 */
159static int
160cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
161 PermissionRequestList *permissions)
162{
163 UNUSED(cookie);
164 CHECK_WORDS();
165 int nerr = 0;
166 bool recurse;
167
168 if (argc < 1) {
169 LOGE("Command %s requires at least one argument\n", name);
170 return 1;
171 }
172
173 recurse = (strcmp(name, "delete_recursive") == 0);
174 ui_print("Deleting files...\n");
175//xxx permissions
176
177 int i;
178 for (i = 0; i < argc; i++) {
179 const char *root_path = argv[i];
180 char pathbuf[PATH_MAX];
181 const char *path;
182
183 /* This guarantees that all paths use "SYSTEM:"-style roots;
184 * plain paths won't make it through translate_root_path().
185 */
186 path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
187 if (path != NULL) {
188 int ret = ensure_root_path_mounted(root_path);
189 if (ret < 0) {
190 LOGW("Can't mount volume to delete \"%s\"\n", root_path);
191 nerr++;
192 continue;
193 }
194 if (recurse) {
195 ret = dirUnlinkHierarchy(path);
196 } else {
197 ret = unlink(path);
198 }
199 if (ret != 0 && errno != ENOENT) {
200 LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
201 nerr++;
202 }
203 } else {
204 nerr++;
205 }
206 }
207//TODO: add a way to fail if a delete didn't work
208
209 return 0;
210}
211
212typedef struct {
213 int num_done;
214 int num_total;
215} ExtractContext;
216
217static void extract_count_cb(const char *fn, void *cookie)
218{
219 ++((ExtractContext*) cookie)->num_total;
220}
221
222static void extract_cb(const char *fn, void *cookie)
223{
224 // minzip writes the filename to the log, so we don't need to
225 ExtractContext *ctx = (ExtractContext*) cookie;
226 ui_set_progress((float) ++ctx->num_done / ctx->num_total);
227}
228
229/* copy_dir <src-dir> <dst-dir> [<timestamp>]
230 *
231 * The contents of <src-dir> will become the contents of <dst-dir>.
232 * The original contents of <dst-dir> are preserved unless something
233 * in <src-dir> overwrote them.
234 *
235 * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
236 * would be copied to "SYSTEM:a".
237 *
238 * The specified timestamp (in decimal seconds since 1970) will be used,
239 * or a fixed default timestamp will be supplied otherwise.
240 */
241static int
242cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
243 PermissionRequestList *permissions)
244{
245 UNUSED(name);
246 UNUSED(cookie);
247 CHECK_WORDS();
248//xxx permissions
249
250 // To create a consistent system image, never use the clock for timestamps.
251 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
252 if (argc == 3) {
253 char *end;
254 time_t value = strtoul(argv[2], &end, 0);
255 if (value == 0 || end[0] != '\0') {
256 LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
257 return 1;
258 } else if (value < timestamp.modtime) {
259 LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
260 return 1;
261 }
262 timestamp.modtime = timestamp.actime = value;
263 } else if (argc != 2) {
264 LOGE("Command %s requires exactly two arguments\n", name);
265 return 1;
266 }
267
268 // Use 40% of the progress bar (80% post-verification) by default
269 ui_print("Copying files...\n");
270 if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
271
272 /* Mount the destination volume if it isn't already.
273 */
274 const char *dst_root_path = argv[1];
275 int ret = ensure_root_path_mounted(dst_root_path);
276 if (ret < 0) {
277 LOGE("Can't mount %s\n", dst_root_path);
278 return 1;
279 }
280
281 /* Get the real target path.
282 */
283 char dstpathbuf[PATH_MAX];
284 const char *dst_path;
285 dst_path = translate_root_path(dst_root_path,
286 dstpathbuf, sizeof(dstpathbuf));
287 if (dst_path == NULL) {
288 LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
289 return 1;
290 }
291
292 /* Try to copy the directory. The source may be inside a package.
293 */
294 const char *src_root_path = argv[0];
295 char srcpathbuf[PATH_MAX];
296 const char *src_path;
297 if (is_package_root_path(src_root_path)) {
298 const ZipArchive *package;
299 src_path = translate_package_root_path(src_root_path,
300 srcpathbuf, sizeof(srcpathbuf), &package);
301 if (src_path == NULL) {
302 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
303 return 1;
304 }
305
306 /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files
307 * are validated by the signature. Do a dry run first to count how
308 * many there are (and find some errors early).
309 */
310 ExtractContext ctx;
311 ctx.num_done = 0;
312 ctx.num_total = 0;
313
314 if (!mzExtractRecursive(package, src_path, dst_path,
315 MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
316 &timestamp, extract_count_cb, (void *) &ctx) ||
317 !mzExtractRecursive(package, src_path, dst_path,
318 MZ_EXTRACT_FILES_ONLY,
319 &timestamp, extract_cb, (void *) &ctx)) {
320 LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
321 name, src_root_path, dst_root_path);
322 return 1;
323 }
324 } else {
325 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
326 name, src_root_path);
327//xxx mount the src volume
328//xxx
329 return 255;
330 }
331
332 return 0;
333}
334
335/* run_program <program-file> [<args> ...]
336 *
337 * Run an external program included in the update package.
338 */
339static int
340cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
341 PermissionRequestList *permissions)
342{
343 UNUSED(cookie);
344 CHECK_WORDS();
345
346 if (argc < 1) {
347 LOGE("Command %s requires at least one argument\n", name);
348 return 1;
349 }
350
351 // Copy the program file to temporary storage.
352 if (!is_package_root_path(argv[0])) {
353 LOGE("Command %s: non-package program file \"%s\" not supported\n",
354 name, argv[0]);
355 return 1;
356 }
357
358 char path[PATH_MAX];
359 const ZipArchive *package;
360 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
361 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
362 return 1;
363 }
364
365 const ZipEntry *entry = mzFindZipEntry(package, path);
366 if (entry == NULL) {
367 LOGE("Can't find %s\n", path);
368 return 1;
369 }
370
371 static const char *binary = "/tmp/run_program_binary";
372 unlink(binary); // just to be sure
373 int fd = creat(binary, 0755);
374 if (fd < 0) {
375 LOGE("Can't make %s\n", binary);
376 return 1;
377 }
378 bool ok = mzExtractZipEntryToFile(package, entry, fd);
379 close(fd);
380
381 if (!ok) {
382 LOGE("Can't copy %s\n", path);
383 return 1;
384 }
385
386 // Create a copy of argv to NULL-terminate it, as execv requires
387 char **args = (char **) malloc(sizeof(char*) * (argc + 1));
388 memcpy(args, argv, sizeof(char*) * argc);
389 args[argc] = NULL;
390
391 pid_t pid = fork();
392 if (pid == 0) {
393 execv(binary, args);
394 fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
395 _exit(-1);
396 }
397
398 int status;
399 waitpid(pid, &status, 0);
Koushik K. Dutta69b75412010-02-23 18:07:31 -0800400 if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800401 return 0;
402 } else {
403 LOGE("Error in %s\n(Status %d)\n", path, status);
404 return 1;
405 }
406}
407
408/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
409 * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
410 *
411 * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
412 * of single files or entire directory trees. Any error causes failure.
413 * User, group, and modes must all be integer values (hex or octal OK).
414 */
415static int
416cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[],
417 PermissionRequestList *permissions)
418{
419 UNUSED(cookie);
420 CHECK_WORDS();
421 bool recurse = !strcmp(name, "set_perm_recursive");
422
423 int min_args = 4 + (recurse ? 1 : 0);
424 if (argc < min_args) {
425 LOGE("Command %s requires at least %d args\n", name, min_args);
426 return 1;
427 }
428
429 // All the arguments except the path(s) are numeric.
430 int i, n[min_args - 1];
431 for (i = 0; i < min_args - 1; ++i) {
432 char *end;
433 n[i] = strtoul(argv[i], &end, 0);
434 if (end[0] != '\0' || argv[i][0] == '\0') {
435 LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
436 return 1;
437 }
438 }
439
440 for (i = min_args - 1; i < min_args; ++i) {
441 char path[PATH_MAX];
442 if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
443 LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
444 return 1;
445 }
446
447 if (ensure_root_path_mounted(argv[i])) {
448 LOGE("Can't mount %s\n", argv[i]);
449 return 1;
450 }
451
452 if (recurse
453 ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
454 : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
455 LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
456 return 1;
457 }
458 }
459
460 return 0;
461}
462
463/* show_progress <fraction> <duration>
464 *
465 * Use <fraction> of the on-screen progress meter for the next operation,
466 * automatically advancing the meter over <duration> seconds (or more rapidly
467 * if the actual rate of progress can be determined).
468 */
469static int
470cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[],
471 PermissionRequestList *permissions)
472{
473 UNUSED(cookie);
474 CHECK_WORDS();
475
476 if (argc != 2) {
477 LOGE("Command %s requires exactly two arguments\n", name);
478 return 1;
479 }
480
481 char *end;
482 double fraction = strtod(argv[0], &end);
483 if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
484 LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
485 return 1;
486 }
487
488 int duration = strtoul(argv[1], &end, 0);
489 if (end[0] != '\0' || argv[0][0] == '\0') {
490 LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
491 return 1;
492 }
493
494 // Half of the progress bar is taken by verification,
495 // so everything that happens during installation is scaled.
496 ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
497 gDidShowProgress = 1;
498 return 0;
499}
500
501/* symlink <link-target> <link-path>
502 *
503 * Create a symlink, like "ln -s". The link path must not exist already.
504 * Note that <link-path> is in root:path format, but <link-target> is
505 * for the target filesystem (and may be relative).
506 */
507static int
508cmd_symlink(const char *name, void *cookie, int argc, const char *argv[],
509 PermissionRequestList *permissions)
510{
511 UNUSED(cookie);
512 CHECK_WORDS();
513
514 if (argc != 2) {
515 LOGE("Command %s requires exactly two arguments\n", name);
516 return 1;
517 }
518
519 char path[PATH_MAX];
520 if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
521 LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
522 return 1;
523 }
524
525 if (ensure_root_path_mounted(argv[1])) {
526 LOGE("Can't mount %s\n", argv[1]);
527 return 1;
528 }
529
530 if (symlink(argv[0], path)) {
531 LOGE("Can't symlink %s\n", path);
532 return 1;
533 }
534
535 return 0;
536}
537
538struct FirmwareContext {
539 size_t total_bytes, done_bytes;
540 char *data;
541};
542
543static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
544{
545 struct FirmwareContext *context = (struct FirmwareContext*) cookie;
546 if (context->done_bytes + data_len > context->total_bytes) {
547 LOGE("Data overrun in firmware\n");
548 return false; // Should not happen, but let's be safe.
549 }
550
551 memcpy(context->data + context->done_bytes, data, data_len);
552 context->done_bytes += data_len;
553 ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
554 return true;
555}
556
557/* write_radio_image <src-image>
558 * write_hboot_image <src-image>
559 * Doesn't actually take effect until the rest of installation finishes.
560 */
561static int
562cmd_write_firmware_image(const char *name, void *cookie,
563 int argc, const char *argv[], PermissionRequestList *permissions)
564{
565 UNUSED(cookie);
566 CHECK_WORDS();
567
568 if (argc != 1) {
569 LOGE("Command %s requires exactly one argument\n", name);
570 return 1;
571 }
572
573 const char *type;
574 if (!strcmp(name, "write_radio_image")) {
575 type = "radio";
576 } else if (!strcmp(name, "write_hboot_image")) {
577 type = "hboot";
578 } else {
579 LOGE("Unknown firmware update command %s\n", name);
580 return 1;
581 }
582
583 if (!is_package_root_path(argv[0])) {
584 LOGE("Command %s: non-package image file \"%s\" not supported\n",
585 name, argv[0]);
586 return 1;
587 }
588
589 ui_print("Extracting %s image...\n", type);
590 char path[PATH_MAX];
591 const ZipArchive *package;
592 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
593 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
594 return 1;
595 }
596
597 const ZipEntry *entry = mzFindZipEntry(package, path);
598 if (entry == NULL) {
599 LOGE("Can't find %s\n", path);
600 return 1;
601 }
602
603 // Load the update image into RAM.
604 struct FirmwareContext context;
605 context.total_bytes = mzGetZipEntryUncompLen(entry);
606 context.done_bytes = 0;
607 context.data = malloc(context.total_bytes);
608 if (context.data == NULL) {
609 LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
610 return 1;
611 }
612
613 if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
614 context.done_bytes != context.total_bytes) {
615 LOGE("Can't read %s\n", argv[0]);
616 free(context.data);
617 return 1;
618 }
619
620 if (remember_firmware_update(type, context.data, context.total_bytes)) {
621 LOGE("Can't store %s image\n", type);
622 free(context.data);
623 return 1;
624 }
625
626 return 0;
627}
628
629static bool write_raw_image_process_fn(
630 const unsigned char *data,
631 int data_len, void *ctx)
632{
633 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
634 if (r == data_len) return true;
635 LOGE("%s\n", strerror(errno));
636 return false;
637}
638
639/* write_raw_image <src-image> <dest-root>
640 */
641static int
642cmd_write_raw_image(const char *name, void *cookie,
643 int argc, const char *argv[], PermissionRequestList *permissions)
644{
645 UNUSED(cookie);
646 CHECK_WORDS();
647//xxx permissions
648
649 if (argc != 2) {
650 LOGE("Command %s requires exactly two arguments\n", name);
651 return 1;
652 }
653
654 // Use 10% of the progress bar (20% post-verification) by default
655 const char *src_root_path = argv[0];
656 const char *dst_root_path = argv[1];
657 ui_print("Writing %s...\n", dst_root_path);
658 if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
659
660 /* Find the source image, which is probably in a package.
661 */
662 if (!is_package_root_path(src_root_path)) {
663 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
664 name, src_root_path);
665 return 255;
666 }
667
668 /* Get the package.
669 */
670 char srcpathbuf[PATH_MAX];
671 const char *src_path;
672 const ZipArchive *package;
673 src_path = translate_package_root_path(src_root_path,
674 srcpathbuf, sizeof(srcpathbuf), &package);
675 if (src_path == NULL) {
676 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
677 return 1;
678 }
679
680 /* Get the entry.
681 */
682 const ZipEntry *entry = mzFindZipEntry(package, src_path);
683 if (entry == NULL) {
684 LOGE("Missing file %s\n", src_path);
685 return 1;
686 }
687
688 /* Unmount the destination root if it isn't already.
689 */
690 int ret = ensure_root_path_unmounted(dst_root_path);
691 if (ret < 0) {
692 LOGE("Can't unmount %s\n", dst_root_path);
693 return 1;
694 }
695
696 /* Open the partition for writing.
697 */
698 const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
699 if (partition == NULL) {
700 LOGE("Can't find %s\n", dst_root_path);
701 return 1;
702 }
703 MtdWriteContext *context = mtd_write_partition(partition);
704 if (context == NULL) {
705 LOGE("Can't open %s\n", dst_root_path);
706 return 1;
707 }
708
709 /* Extract and write the image.
710 */
711 bool ok = mzProcessZipEntryContents(package, entry,
712 write_raw_image_process_fn, context);
713 if (!ok) {
714 LOGE("Error writing %s\n", dst_root_path);
715 mtd_write_close(context);
716 return 1;
717 }
718
719 if (mtd_erase_blocks(context, -1) == (off_t) -1) {
720 LOGE("Error finishing %s\n", dst_root_path);
721 mtd_write_close(context);
722 return -1;
723 }
724
725 if (mtd_write_close(context)) {
726 LOGE("Error closing %s\n", dst_root_path);
727 return -1;
728 }
729 return 0;
730}
731
732/* mark <resource> dirty|clean
733 */
734static int
735cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
736 PermissionRequestList *permissions)
737{
738 UNUSED(name);
739 UNUSED(cookie);
740 CHECK_WORDS();
741//xxx when marking, save the top-level hash at the mark point
742// so we can retry on failure. Otherwise the hashes won't match,
743// or someone could intentionally dirty the FS to force a downgrade
744//xxx
745 return -1;
746}
747
748/* done
749 */
750static int
751cmd_done(const char *name, void *cookie, int argc, const char *argv[],
752 PermissionRequestList *permissions)
753{
754 UNUSED(name);
755 UNUSED(cookie);
756 CHECK_WORDS();
757//xxx
758 return -1;
759}
760
761
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800762static int
763cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
764 PermissionRequestList *permissions)
765{
766 UNUSED(cookie);
767 CHECK_WORDS();
768
769 char* backup_name = NULL;
770 switch(argc)
771 {
772 case 0:
773 break;
774 case 1:
775 backup_name = argv[0];
776 break;
777 default:
778 LOGE("Command %s requires zero or one argument\n", name);
779 return 1;
780 }
781
782 return do_nandroid_backup(backup_name);
783}
784
785static int
786cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
787 PermissionRequestList *permissions)
788{
789 UNUSED(cookie);
790 CHECK_WORDS();
791
792 if (argc != 1) {
793 LOGE("Command %s requires exactly one argument\n", name);
794 return 1;
795 }
796
797 return do_nandroid_restore(argv[0]);
798}
799
800
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800801/*
802 * Function definitions
803 */
804
805/* compatible_with(<version>)
806 *
807 * Returns "true" if this version of the script parser and command
808 * set supports the named version.
809 */
810static int
811fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
812 char **result, size_t *resultLen,
813 PermissionRequestList *permissions)
814{
815 UNUSED(name);
816 UNUSED(cookie);
817 CHECK_FN();
818 NO_PERMS(permissions);
819
820 if (argc != 1) {
821 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
822 name, argc);
823 return 1;
824 }
825
826 if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
827 *result = strdup("true");
828 } else {
829 *result = strdup("");
830 }
831 if (resultLen != NULL) {
832 *resultLen = strlen(*result);
833 }
834 return 0;
835}
836
837/* update_forced()
838 *
839 * Returns "true" if some system setting has determined that
840 * the update should happen no matter what.
841 */
842static int
843fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
844 char **result, size_t *resultLen,
845 PermissionRequestList *permissions)
846{
847 UNUSED(name);
848 UNUSED(cookie);
849 CHECK_FN();
850 NO_PERMS(permissions);
851
852 if (argc != 0) {
853 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
854 name, argc);
855 return 1;
856 }
857
858 //xxx check some global or property
859 bool force = true;
860 if (force) {
861 *result = strdup("true");
862 } else {
863 *result = strdup("");
864 }
865 if (resultLen != NULL) {
866 *resultLen = strlen(*result);
867 }
868
869 return 0;
870}
871
872/* get_mark(<resource>)
873 *
874 * Returns the current mark associated with the provided resource.
875 */
876static int
877fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
878 char **result, size_t *resultLen,
879 PermissionRequestList *permissions)
880{
881 UNUSED(name);
882 UNUSED(cookie);
883 CHECK_FN();
884 NO_PERMS(permissions);
885
886 if (argc != 1) {
887 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
888 name, argc);
889 return 1;
890 }
891
892 //xxx look up the value
893 *result = strdup("");
894 if (resultLen != NULL) {
895 *resultLen = strlen(*result);
896 }
897
898 return 0;
899}
900
901/* hash_dir(<path-to-directory>)
902 */
903static int
904fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
905 char **result, size_t *resultLen,
906 PermissionRequestList *permissions)
907{
908 int ret = -1;
909
910 UNUSED(name);
911 UNUSED(cookie);
912 CHECK_FN();
913
914 const char *dir;
915 if (argc != 1) {
916 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
917 name, argc);
918 return 1;
919 } else {
920 dir = argv[0];
921 }
922
923 if (permissions != NULL) {
924 if (dir == NULL) {
925 /* The argument is the result of another function.
926 * Assume the worst case, where the function returns
927 * the root.
928 */
929 dir = "/";
930 }
931 ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
932 } else {
933//xxx build and return the string
934 *result = strdup("hashvalue");
935 if (resultLen != NULL) {
936 *resultLen = strlen(*result);
937 }
938 ret = 0;
939 }
940
941 return ret;
942}
943
944/* matches(<str>, <str1> [, <strN>...])
945 * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
946 * otherwise returns "".
947 *
948 * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
949 */
950static int
951fn_matches(const char *name, void *cookie, int argc, const char *argv[],
952 char **result, size_t *resultLen,
953 PermissionRequestList *permissions)
954{
955 UNUSED(name);
956 UNUSED(cookie);
957 CHECK_FN();
958 NO_PERMS(permissions);
959
960 if (argc < 2) {
961 fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
962 name, argc);
963 return 1;
964 }
965
966 int i;
967 for (i = 1; i < argc; i++) {
968 if (strcmp(argv[0], argv[i]) == 0) {
969 *result = strdup(argv[0]);
970 if (resultLen != NULL) {
971 *resultLen = strlen(*result);
972 }
973 return 0;
974 }
975 }
976
977 *result = strdup("");
978 if (resultLen != NULL) {
979 *resultLen = 1;
980 }
981 return 0;
982}
983
984/* concat(<str>, <str1> [, <strN>...])
985 * Returns the concatenation of all strings.
986 */
987static int
988fn_concat(const char *name, void *cookie, int argc, const char *argv[],
989 char **result, size_t *resultLen,
990 PermissionRequestList *permissions)
991{
992 UNUSED(name);
993 UNUSED(cookie);
994 CHECK_FN();
995 NO_PERMS(permissions);
996
997 size_t totalLen = 0;
998 int i;
999 for (i = 0; i < argc; i++) {
1000 totalLen += strlen(argv[i]);
1001 }
1002
1003 char *s = (char *)malloc(totalLen + 1);
1004 if (s == NULL) {
1005 return -1;
1006 }
1007 s[totalLen] = '\0';
1008 for (i = 0; i < argc; i++) {
1009 //TODO: keep track of the end to avoid walking the string each time
1010 strcat(s, argv[i]);
1011 }
1012 *result = s;
1013 if (resultLen != NULL) {
1014 *resultLen = strlen(s);
1015 }
1016
1017 return 0;
1018}
1019
1020/* getprop(<property>)
1021 * Returns the named Android system property value, or "" if not set.
1022 */
1023static int
1024fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
1025 char **result, size_t *resultLen,
1026 PermissionRequestList *permissions)
1027{
1028 UNUSED(cookie);
1029 CHECK_FN();
1030 NO_PERMS(permissions);
1031
1032 if (argc != 1) {
1033 LOGE("Command %s requires exactly one argument\n", name);
1034 return 1;
1035 }
1036
1037 char value[PROPERTY_VALUE_MAX];
1038 property_get(argv[0], value, "");
1039
1040 *result = strdup(value);
1041 if (resultLen != NULL) {
1042 *resultLen = strlen(*result);
1043 }
1044
1045 return 0;
1046}
1047
1048/* file_contains(<filename>, <substring>)
1049 * Returns "true" if the file exists and contains the specified substring.
1050 */
1051static int
1052fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
1053 char **result, size_t *resultLen,
1054 PermissionRequestList *permissions)
1055{
1056 UNUSED(cookie);
1057 CHECK_FN();
1058 NO_PERMS(permissions);
1059
1060 if (argc != 2) {
1061 LOGE("Command %s requires exactly two arguments\n", name);
1062 return 1;
1063 }
1064
1065 char pathbuf[PATH_MAX];
1066 const char *root_path = argv[0];
1067 const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
1068 if (path == NULL) {
1069 LOGE("Command %s: bad path \"%s\"\n", name, root_path);
1070 return 1;
1071 }
1072
1073 if (ensure_root_path_mounted(root_path)) {
1074 LOGE("Can't mount %s\n", root_path);
1075 return 1;
1076 }
1077
1078 const char *needle = argv[1];
1079 char *haystack = (char*) load_file(path, NULL);
1080 if (haystack == NULL) {
1081 LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
1082 *result = ""; /* File not found is not an error. */
1083 } else if (strstr(haystack, needle) == NULL) {
1084 LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
1085 *result = strdup("");
1086 free(haystack);
1087 } else {
1088 *result = strdup("true");
1089 free(haystack);
1090 }
1091
1092 if (resultLen != NULL) {
1093 *resultLen = strlen(*result);
1094 }
1095 return 0;
1096}
1097
1098int
1099register_update_commands(RecoveryCommandContext *ctx)
1100{
1101 int ret;
1102
1103 ret = commandInit();
1104 if (ret < 0) return ret;
1105
1106 /*
1107 * Commands
1108 */
1109
1110 ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
1111 if (ret < 0) return ret;
1112
1113 ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
1114 if (ret < 0) return ret;
1115
1116 ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
1117 (void *)ctx);
1118 if (ret < 0) return ret;
1119
1120 ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
1121 cmd_copy_dir, (void *)ctx);
1122 if (ret < 0) return ret;
1123
1124 ret = registerCommand("run_program", CMD_ARGS_WORDS,
1125 cmd_run_program, (void *)ctx);
1126 if (ret < 0) return ret;
1127
1128 ret = registerCommand("set_perm", CMD_ARGS_WORDS,
1129 cmd_set_perm, (void *)ctx);
1130 if (ret < 0) return ret;
1131
1132 ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
1133 cmd_set_perm, (void *)ctx);
1134 if (ret < 0) return ret;
1135
1136 ret = registerCommand("show_progress", CMD_ARGS_WORDS,
1137 cmd_show_progress, (void *)ctx);
1138 if (ret < 0) return ret;
1139
1140 ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
1141 if (ret < 0) return ret;
1142
1143 ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
1144 if (ret < 0) return ret;
1145
1146 ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
1147 cmd_write_firmware_image, (void *)ctx);
1148 if (ret < 0) return ret;
1149
1150 ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
1151 cmd_write_firmware_image, (void *)ctx);
1152 if (ret < 0) return ret;
1153
1154 ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
1155 cmd_write_raw_image, (void *)ctx);
1156 if (ret < 0) return ret;
1157
1158 ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
1159 if (ret < 0) return ret;
1160
1161 ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
1162 if (ret < 0) return ret;
1163
Koushik K. Duttaa9483082010-03-07 21:47:41 -08001164 ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
1165 if (ret < 0) return ret;
1166
1167 ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
1168 if (ret < 0) return ret;
1169
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -08001170 /*
1171 * Functions
1172 */
1173
1174 ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
1175 if (ret < 0) return ret;
1176
1177 ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
1178 if (ret < 0) return ret;
1179
1180 ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
1181 if (ret < 0) return ret;
1182
1183 ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
1184 if (ret < 0) return ret;
1185
1186 ret = registerFunction("matches", fn_matches, (void *)ctx);
1187 if (ret < 0) return ret;
1188
1189 ret = registerFunction("concat", fn_concat, (void *)ctx);
1190 if (ret < 0) return ret;
1191
1192 ret = registerFunction("getprop", fn_getprop, (void *)ctx);
1193 if (ret < 0) return ret;
1194
1195 ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
1196 if (ret < 0) return ret;
1197
1198 return 0;
1199}