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