blob: c7e4fd778b3a6522b4e488f38cd87f1972561ad5 [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 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800148#ifdef BOARD_HAS_DATADATA
Koushik Dutta63e04762010-06-15 12:56:17 -0700149 if (0 == strcmp(root, "DATA:")) {
150 ret = format_root_device("DATADATA:");
151 if (ret != 0) {
152 LOGE("Can't format %s\n", root);
153 return 1;
154 }
155 }
156#endif
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800157 return 0;
158}
159
160/* delete <file1> [<fileN> ...]
161 * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
162 *
163 * Like "rm -f", will try to delete every named file/dir, even if
164 * earlier ones fail. Recursive deletes that fail halfway through
165 * give up early.
166 */
167static int
168cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
169 PermissionRequestList *permissions)
170{
171 UNUSED(cookie);
172 CHECK_WORDS();
173 int nerr = 0;
174 bool recurse;
175
176 if (argc < 1) {
177 LOGE("Command %s requires at least one argument\n", name);
178 return 1;
179 }
180
181 recurse = (strcmp(name, "delete_recursive") == 0);
182 ui_print("Deleting files...\n");
183//xxx permissions
184
185 int i;
186 for (i = 0; i < argc; i++) {
187 const char *root_path = argv[i];
188 char pathbuf[PATH_MAX];
189 const char *path;
190
191 /* This guarantees that all paths use "SYSTEM:"-style roots;
192 * plain paths won't make it through translate_root_path().
193 */
194 path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
195 if (path != NULL) {
196 int ret = ensure_root_path_mounted(root_path);
197 if (ret < 0) {
198 LOGW("Can't mount volume to delete \"%s\"\n", root_path);
199 nerr++;
200 continue;
201 }
202 if (recurse) {
203 ret = dirUnlinkHierarchy(path);
204 } else {
205 ret = unlink(path);
206 }
207 if (ret != 0 && errno != ENOENT) {
208 LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
209 nerr++;
210 }
211 } else {
212 nerr++;
213 }
214 }
215//TODO: add a way to fail if a delete didn't work
216
217 return 0;
218}
219
220typedef struct {
221 int num_done;
222 int num_total;
223} ExtractContext;
224
225static void extract_count_cb(const char *fn, void *cookie)
226{
227 ++((ExtractContext*) cookie)->num_total;
228}
229
230static void extract_cb(const char *fn, void *cookie)
231{
232 // minzip writes the filename to the log, so we don't need to
233 ExtractContext *ctx = (ExtractContext*) cookie;
234 ui_set_progress((float) ++ctx->num_done / ctx->num_total);
235}
236
237/* copy_dir <src-dir> <dst-dir> [<timestamp>]
238 *
239 * The contents of <src-dir> will become the contents of <dst-dir>.
240 * The original contents of <dst-dir> are preserved unless something
241 * in <src-dir> overwrote them.
242 *
243 * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
244 * would be copied to "SYSTEM:a".
245 *
246 * The specified timestamp (in decimal seconds since 1970) will be used,
247 * or a fixed default timestamp will be supplied otherwise.
248 */
249static int
250cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
251 PermissionRequestList *permissions)
252{
253 UNUSED(name);
254 UNUSED(cookie);
255 CHECK_WORDS();
256//xxx permissions
257
258 // To create a consistent system image, never use the clock for timestamps.
259 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
260 if (argc == 3) {
261 char *end;
262 time_t value = strtoul(argv[2], &end, 0);
263 if (value == 0 || end[0] != '\0') {
264 LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
265 return 1;
266 } else if (value < timestamp.modtime) {
267 LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
268 return 1;
269 }
270 timestamp.modtime = timestamp.actime = value;
271 } else if (argc != 2) {
272 LOGE("Command %s requires exactly two arguments\n", name);
273 return 1;
274 }
275
276 // Use 40% of the progress bar (80% post-verification) by default
277 ui_print("Copying files...\n");
278 if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
279
280 /* Mount the destination volume if it isn't already.
281 */
282 const char *dst_root_path = argv[1];
283 int ret = ensure_root_path_mounted(dst_root_path);
284 if (ret < 0) {
285 LOGE("Can't mount %s\n", dst_root_path);
286 return 1;
287 }
288
289 /* Get the real target path.
290 */
291 char dstpathbuf[PATH_MAX];
292 const char *dst_path;
293 dst_path = translate_root_path(dst_root_path,
294 dstpathbuf, sizeof(dstpathbuf));
295 if (dst_path == NULL) {
296 LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
297 return 1;
298 }
299
300 /* Try to copy the directory. The source may be inside a package.
301 */
302 const char *src_root_path = argv[0];
303 char srcpathbuf[PATH_MAX];
304 const char *src_path;
305 if (is_package_root_path(src_root_path)) {
306 const ZipArchive *package;
307 src_path = translate_package_root_path(src_root_path,
308 srcpathbuf, sizeof(srcpathbuf), &package);
309 if (src_path == NULL) {
310 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
311 return 1;
312 }
313
314 /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files
315 * are validated by the signature. Do a dry run first to count how
316 * many there are (and find some errors early).
317 */
318 ExtractContext ctx;
319 ctx.num_done = 0;
320 ctx.num_total = 0;
321
322 if (!mzExtractRecursive(package, src_path, dst_path,
323 MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
324 &timestamp, extract_count_cb, (void *) &ctx) ||
325 !mzExtractRecursive(package, src_path, dst_path,
326 MZ_EXTRACT_FILES_ONLY,
327 &timestamp, extract_cb, (void *) &ctx)) {
328 LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
329 name, src_root_path, dst_root_path);
330 return 1;
331 }
332 } else {
333 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
334 name, src_root_path);
335//xxx mount the src volume
336//xxx
337 return 255;
338 }
339
340 return 0;
341}
342
343/* run_program <program-file> [<args> ...]
344 *
345 * Run an external program included in the update package.
346 */
347static int
348cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
349 PermissionRequestList *permissions)
350{
351 UNUSED(cookie);
352 CHECK_WORDS();
353
354 if (argc < 1) {
355 LOGE("Command %s requires at least one argument\n", name);
356 return 1;
357 }
358
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800359 static const char *binary = "/tmp/run_program_binary";
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700360 char path[PATH_MAX];
361 if (!is_package_root_path(argv[0])) {
362 // Copy the program file to temporary storage.
363 binary = argv[0];
364 strcpy(path, binary);
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800365 }
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700366 else
367 {
368 const ZipArchive *package;
369 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
370 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
371 return 1;
372 }
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800373
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700374 const ZipEntry *entry = mzFindZipEntry(package, path);
375 if (entry == NULL) {
376 LOGE("Can't find %s\n", path);
377 return 1;
378 }
379
380 unlink(binary); // just to be sure
381 int fd = creat(binary, 0755);
382 if (fd < 0) {
383 LOGE("Can't make %s\n", binary);
384 return 1;
385 }
386 bool ok = mzExtractZipEntryToFile(package, entry, fd);
387 close(fd);
388
389 if (!ok) {
390 LOGE("Can't copy %s\n", path);
391 return 1;
392 }
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800393 }
394
395 // Create a copy of argv to NULL-terminate it, as execv requires
396 char **args = (char **) malloc(sizeof(char*) * (argc + 1));
397 memcpy(args, argv, sizeof(char*) * argc);
398 args[argc] = NULL;
399
400 pid_t pid = fork();
401 if (pid == 0) {
402 execv(binary, args);
403 fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
404 _exit(-1);
405 }
406
407 int status;
408 waitpid(pid, &status, 0);
Koushik K. Dutta69b75412010-02-23 18:07:31 -0800409 if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800410 return 0;
411 } else {
412 LOGE("Error in %s\n(Status %d)\n", path, status);
413 return 1;
414 }
415}
416
417/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
418 * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
419 *
420 * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
421 * of single files or entire directory trees. Any error causes failure.
422 * User, group, and modes must all be integer values (hex or octal OK).
423 */
424static int
425cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[],
426 PermissionRequestList *permissions)
427{
428 UNUSED(cookie);
429 CHECK_WORDS();
430 bool recurse = !strcmp(name, "set_perm_recursive");
431
432 int min_args = 4 + (recurse ? 1 : 0);
433 if (argc < min_args) {
434 LOGE("Command %s requires at least %d args\n", name, min_args);
435 return 1;
436 }
437
438 // All the arguments except the path(s) are numeric.
439 int i, n[min_args - 1];
440 for (i = 0; i < min_args - 1; ++i) {
441 char *end;
442 n[i] = strtoul(argv[i], &end, 0);
443 if (end[0] != '\0' || argv[i][0] == '\0') {
444 LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
445 return 1;
446 }
447 }
448
449 for (i = min_args - 1; i < min_args; ++i) {
450 char path[PATH_MAX];
451 if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
452 LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
453 return 1;
454 }
455
456 if (ensure_root_path_mounted(argv[i])) {
457 LOGE("Can't mount %s\n", argv[i]);
458 return 1;
459 }
460
461 if (recurse
462 ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
463 : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
464 LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
465 return 1;
466 }
467 }
468
469 return 0;
470}
471
472/* show_progress <fraction> <duration>
473 *
474 * Use <fraction> of the on-screen progress meter for the next operation,
475 * automatically advancing the meter over <duration> seconds (or more rapidly
476 * if the actual rate of progress can be determined).
477 */
478static int
479cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[],
480 PermissionRequestList *permissions)
481{
482 UNUSED(cookie);
483 CHECK_WORDS();
484
485 if (argc != 2) {
486 LOGE("Command %s requires exactly two arguments\n", name);
487 return 1;
488 }
489
490 char *end;
491 double fraction = strtod(argv[0], &end);
492 if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
493 LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
494 return 1;
495 }
496
497 int duration = strtoul(argv[1], &end, 0);
498 if (end[0] != '\0' || argv[0][0] == '\0') {
499 LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
500 return 1;
501 }
502
503 // Half of the progress bar is taken by verification,
504 // so everything that happens during installation is scaled.
505 ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
506 gDidShowProgress = 1;
507 return 0;
508}
509
510/* symlink <link-target> <link-path>
511 *
512 * Create a symlink, like "ln -s". The link path must not exist already.
513 * Note that <link-path> is in root:path format, but <link-target> is
514 * for the target filesystem (and may be relative).
515 */
516static int
517cmd_symlink(const char *name, void *cookie, int argc, const char *argv[],
518 PermissionRequestList *permissions)
519{
520 UNUSED(cookie);
521 CHECK_WORDS();
522
523 if (argc != 2) {
524 LOGE("Command %s requires exactly two arguments\n", name);
525 return 1;
526 }
527
528 char path[PATH_MAX];
529 if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
530 LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
531 return 1;
532 }
533
534 if (ensure_root_path_mounted(argv[1])) {
535 LOGE("Can't mount %s\n", argv[1]);
536 return 1;
537 }
538
539 if (symlink(argv[0], path)) {
540 LOGE("Can't symlink %s\n", path);
541 return 1;
542 }
543
544 return 0;
545}
546
547struct FirmwareContext {
548 size_t total_bytes, done_bytes;
549 char *data;
550};
551
552static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
553{
554 struct FirmwareContext *context = (struct FirmwareContext*) cookie;
555 if (context->done_bytes + data_len > context->total_bytes) {
556 LOGE("Data overrun in firmware\n");
557 return false; // Should not happen, but let's be safe.
558 }
559
560 memcpy(context->data + context->done_bytes, data, data_len);
561 context->done_bytes += data_len;
562 ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
563 return true;
564}
565
566/* write_radio_image <src-image>
567 * write_hboot_image <src-image>
568 * Doesn't actually take effect until the rest of installation finishes.
569 */
570static int
571cmd_write_firmware_image(const char *name, void *cookie,
572 int argc, const char *argv[], PermissionRequestList *permissions)
573{
574 UNUSED(cookie);
575 CHECK_WORDS();
576
577 if (argc != 1) {
578 LOGE("Command %s requires exactly one argument\n", name);
579 return 1;
580 }
581
582 const char *type;
583 if (!strcmp(name, "write_radio_image")) {
584 type = "radio";
585 } else if (!strcmp(name, "write_hboot_image")) {
586 type = "hboot";
587 } else {
588 LOGE("Unknown firmware update command %s\n", name);
589 return 1;
590 }
591
592 if (!is_package_root_path(argv[0])) {
593 LOGE("Command %s: non-package image file \"%s\" not supported\n",
594 name, argv[0]);
595 return 1;
596 }
597
598 ui_print("Extracting %s image...\n", type);
599 char path[PATH_MAX];
600 const ZipArchive *package;
601 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
602 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
603 return 1;
604 }
605
606 const ZipEntry *entry = mzFindZipEntry(package, path);
607 if (entry == NULL) {
608 LOGE("Can't find %s\n", path);
609 return 1;
610 }
611
612 // Load the update image into RAM.
613 struct FirmwareContext context;
614 context.total_bytes = mzGetZipEntryUncompLen(entry);
615 context.done_bytes = 0;
616 context.data = malloc(context.total_bytes);
617 if (context.data == NULL) {
618 LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
619 return 1;
620 }
621
622 if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
623 context.done_bytes != context.total_bytes) {
624 LOGE("Can't read %s\n", argv[0]);
625 free(context.data);
626 return 1;
627 }
628
Koushik Dutta5aaa8232010-07-20 16:23:18 -0700629#ifndef BOARD_HAS_NO_MISC_PARTITION
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800630 if (remember_firmware_update(type, context.data, context.total_bytes)) {
631 LOGE("Can't store %s image\n", type);
632 free(context.data);
633 return 1;
634 }
Koushik Dutta5aaa8232010-07-20 16:23:18 -0700635#endif
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800636
637 return 0;
638}
639
640static bool write_raw_image_process_fn(
641 const unsigned char *data,
642 int data_len, void *ctx)
643{
644 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
645 if (r == data_len) return true;
646 LOGE("%s\n", strerror(errno));
647 return false;
648}
649
650/* write_raw_image <src-image> <dest-root>
651 */
652static int
653cmd_write_raw_image(const char *name, void *cookie,
654 int argc, const char *argv[], PermissionRequestList *permissions)
655{
Koushik Dutta19447c02010-11-10 10:40:44 -0800656#ifdef BOARD_USES_MTDUTILS
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800657 UNUSED(cookie);
658 CHECK_WORDS();
659//xxx permissions
660
661 if (argc != 2) {
662 LOGE("Command %s requires exactly two arguments\n", name);
663 return 1;
664 }
665
666 // Use 10% of the progress bar (20% post-verification) by default
667 const char *src_root_path = argv[0];
668 const char *dst_root_path = argv[1];
669 ui_print("Writing %s...\n", dst_root_path);
670 if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
671
672 /* Find the source image, which is probably in a package.
673 */
674 if (!is_package_root_path(src_root_path)) {
675 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
676 name, src_root_path);
677 return 255;
678 }
679
680 /* Get the package.
681 */
682 char srcpathbuf[PATH_MAX];
683 const char *src_path;
684 const ZipArchive *package;
685 src_path = translate_package_root_path(src_root_path,
686 srcpathbuf, sizeof(srcpathbuf), &package);
687 if (src_path == NULL) {
688 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
689 return 1;
690 }
691
692 /* Get the entry.
693 */
694 const ZipEntry *entry = mzFindZipEntry(package, src_path);
695 if (entry == NULL) {
696 LOGE("Missing file %s\n", src_path);
697 return 1;
698 }
699
700 /* Unmount the destination root if it isn't already.
701 */
702 int ret = ensure_root_path_unmounted(dst_root_path);
703 if (ret < 0) {
704 LOGE("Can't unmount %s\n", dst_root_path);
705 return 1;
706 }
707
708 /* Open the partition for writing.
709 */
710 const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
711 if (partition == NULL) {
712 LOGE("Can't find %s\n", dst_root_path);
713 return 1;
714 }
715 MtdWriteContext *context = mtd_write_partition(partition);
716 if (context == NULL) {
717 LOGE("Can't open %s\n", dst_root_path);
718 return 1;
719 }
720
721 /* Extract and write the image.
722 */
723 bool ok = mzProcessZipEntryContents(package, entry,
724 write_raw_image_process_fn, context);
725 if (!ok) {
726 LOGE("Error writing %s\n", dst_root_path);
727 mtd_write_close(context);
728 return 1;
729 }
730
731 if (mtd_erase_blocks(context, -1) == (off_t) -1) {
732 LOGE("Error finishing %s\n", dst_root_path);
733 mtd_write_close(context);
734 return -1;
735 }
736
737 if (mtd_write_close(context)) {
738 LOGE("Error closing %s\n", dst_root_path);
739 return -1;
740 }
741 return 0;
Koushik Dutta19447c02010-11-10 10:40:44 -0800742#else
743 LOGE("Board does not support mtd utils.");
744 return -1;
745#endif
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800746}
747
748/* mark <resource> dirty|clean
749 */
750static int
751cmd_mark(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 when marking, save the top-level hash at the mark point
758// so we can retry on failure. Otherwise the hashes won't match,
759// or someone could intentionally dirty the FS to force a downgrade
760//xxx
761 return -1;
762}
763
764/* done
765 */
766static int
767cmd_done(const char *name, void *cookie, int argc, const char *argv[],
768 PermissionRequestList *permissions)
769{
770 UNUSED(name);
771 UNUSED(cookie);
772 CHECK_WORDS();
773//xxx
774 return -1;
775}
776
777
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800778static int
779cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
780 PermissionRequestList *permissions)
781{
782 UNUSED(cookie);
783 CHECK_WORDS();
784
785 char* backup_name = NULL;
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700786 char backup_path[PATH_MAX];
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800787 switch(argc)
788 {
789 case 0:
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700790 {
Koushik K. Dutta6a26e7c2010-03-27 15:26:11 -0700791 char backup_path[PATH_MAX];
Koushik Dutta08370912010-06-26 12:25:02 -0700792 nandroid_generate_timestamp_path(backup_path);
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700793 backup_name = backup_path;
794 }
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800795 break;
796 case 1:
797 backup_name = argv[0];
798 break;
799 default:
800 LOGE("Command %s requires zero or one argument\n", name);
801 return 1;
802 }
803
Koushik K. Duttaee57bbc2010-03-12 23:21:12 -0800804 return nandroid_backup(backup_name);
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800805}
806
807static int
808cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
809 PermissionRequestList *permissions)
810{
811 UNUSED(cookie);
812 CHECK_WORDS();
813
Koushik K. Dutta6923cc32010-03-29 14:46:00 -0700814 int restoreboot = 1;
815 int restoresystem = 1;
816 int restoredata = 1;
817 int restorecache = 1;
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700818 int restoresdext = 1;
Koushik K. Dutta6923cc32010-03-29 14:46:00 -0700819 int i;
820 for (i = 0; i < argc; i++)
821 {
822 if (strcmp(argv[i], "noboot") == 0)
823 restoreboot = 0;
824 else if (strcmp(argv[i], "nosystem") == 0)
825 restoresystem = 0;
826 else if (strcmp(argv[i], "nodata") == 0)
827 restoredata = 0;
828 else if (strcmp(argv[i], "nocache") == 0)
829 restorecache = 0;
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700830 else if (strcmp(argv[i], "nosd-ext") == 0)
831 restorecache = 0;
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800832 }
833
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700834 return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext);
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800835}
836
Koushik K. Dutta60d7ee02010-03-07 22:16:55 -0800837static int
838cmd_sleep(const char *name, void *cookie, int argc, const char *argv[],
839 PermissionRequestList *permissions)
840{
841 UNUSED(cookie);
842 CHECK_WORDS();
843
844 if (argc != 1) {
845 LOGE("Command %s requires exactly one argument\n", name);
846 return 1;
847 }
848
849 int seconds = atoi(argv[0]);
850 sleep(seconds);
851
852 return 0;
853}
854
855static int
856cmd_print(const char *name, void *cookie, int argc, const char *argv[],
857 PermissionRequestList *permissions)
858{
859 UNUSED(cookie);
860 CHECK_WORDS();
861
862 char message[1024];
863 message[0] = NULL;
864 int i;
865 for (i = 0; i < argc; i++)
866 {
867 strcat(message, argv[i]);
868 strcat(message, " ");
869 }
870 strcat(message, "\n");
871
872 ui_print(message);
873 return 0;
874}
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800875
Koushik K. Duttaea46fe22010-03-08 02:58:04 -0800876static int
877cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[],
878 PermissionRequestList *permissions)
879{
880 UNUSED(cookie);
881 CHECK_WORDS();
882
883 if (argc != 1) {
884 LOGE("Command %s requires exactly one argument\n", name);
885 return 1;
886 }
887
888 return install_zip(argv[0]);
889}
890
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800891/*
892 * Function definitions
893 */
894
895/* compatible_with(<version>)
896 *
897 * Returns "true" if this version of the script parser and command
898 * set supports the named version.
899 */
900static int
901fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
902 char **result, size_t *resultLen,
903 PermissionRequestList *permissions)
904{
905 UNUSED(name);
906 UNUSED(cookie);
907 CHECK_FN();
908 NO_PERMS(permissions);
909
910 if (argc != 1) {
911 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
912 name, argc);
913 return 1;
914 }
915
916 if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
917 *result = strdup("true");
918 } else {
919 *result = strdup("");
920 }
921 if (resultLen != NULL) {
922 *resultLen = strlen(*result);
923 }
924 return 0;
925}
926
927/* update_forced()
928 *
929 * Returns "true" if some system setting has determined that
930 * the update should happen no matter what.
931 */
932static int
933fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
934 char **result, size_t *resultLen,
935 PermissionRequestList *permissions)
936{
937 UNUSED(name);
938 UNUSED(cookie);
939 CHECK_FN();
940 NO_PERMS(permissions);
941
942 if (argc != 0) {
943 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
944 name, argc);
945 return 1;
946 }
947
948 //xxx check some global or property
949 bool force = true;
950 if (force) {
951 *result = strdup("true");
952 } else {
953 *result = strdup("");
954 }
955 if (resultLen != NULL) {
956 *resultLen = strlen(*result);
957 }
958
959 return 0;
960}
961
962/* get_mark(<resource>)
963 *
964 * Returns the current mark associated with the provided resource.
965 */
966static int
967fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
968 char **result, size_t *resultLen,
969 PermissionRequestList *permissions)
970{
971 UNUSED(name);
972 UNUSED(cookie);
973 CHECK_FN();
974 NO_PERMS(permissions);
975
976 if (argc != 1) {
977 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
978 name, argc);
979 return 1;
980 }
981
982 //xxx look up the value
983 *result = strdup("");
984 if (resultLen != NULL) {
985 *resultLen = strlen(*result);
986 }
987
988 return 0;
989}
990
991/* hash_dir(<path-to-directory>)
992 */
993static int
994fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
995 char **result, size_t *resultLen,
996 PermissionRequestList *permissions)
997{
998 int ret = -1;
999
1000 UNUSED(name);
1001 UNUSED(cookie);
1002 CHECK_FN();
1003
1004 const char *dir;
1005 if (argc != 1) {
1006 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
1007 name, argc);
1008 return 1;
1009 } else {
1010 dir = argv[0];
1011 }
1012
1013 if (permissions != NULL) {
1014 if (dir == NULL) {
1015 /* The argument is the result of another function.
1016 * Assume the worst case, where the function returns
1017 * the root.
1018 */
1019 dir = "/";
1020 }
1021 ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
1022 } else {
1023//xxx build and return the string
1024 *result = strdup("hashvalue");
1025 if (resultLen != NULL) {
1026 *resultLen = strlen(*result);
1027 }
1028 ret = 0;
1029 }
1030
1031 return ret;
1032}
1033
1034/* matches(<str>, <str1> [, <strN>...])
1035 * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
1036 * otherwise returns "".
1037 *
1038 * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
1039 */
1040static int
1041fn_matches(const char *name, void *cookie, int argc, const char *argv[],
1042 char **result, size_t *resultLen,
1043 PermissionRequestList *permissions)
1044{
1045 UNUSED(name);
1046 UNUSED(cookie);
1047 CHECK_FN();
1048 NO_PERMS(permissions);
1049
1050 if (argc < 2) {
1051 fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
1052 name, argc);
1053 return 1;
1054 }
1055
1056 int i;
1057 for (i = 1; i < argc; i++) {
1058 if (strcmp(argv[0], argv[i]) == 0) {
1059 *result = strdup(argv[0]);
1060 if (resultLen != NULL) {
1061 *resultLen = strlen(*result);
1062 }
1063 return 0;
1064 }
1065 }
1066
1067 *result = strdup("");
1068 if (resultLen != NULL) {
1069 *resultLen = 1;
1070 }
1071 return 0;
1072}
1073
1074/* concat(<str>, <str1> [, <strN>...])
1075 * Returns the concatenation of all strings.
1076 */
1077static int
1078fn_concat(const char *name, void *cookie, int argc, const char *argv[],
1079 char **result, size_t *resultLen,
1080 PermissionRequestList *permissions)
1081{
1082 UNUSED(name);
1083 UNUSED(cookie);
1084 CHECK_FN();
1085 NO_PERMS(permissions);
1086
1087 size_t totalLen = 0;
1088 int i;
1089 for (i = 0; i < argc; i++) {
1090 totalLen += strlen(argv[i]);
1091 }
1092
1093 char *s = (char *)malloc(totalLen + 1);
1094 if (s == NULL) {
1095 return -1;
1096 }
1097 s[totalLen] = '\0';
1098 for (i = 0; i < argc; i++) {
1099 //TODO: keep track of the end to avoid walking the string each time
1100 strcat(s, argv[i]);
1101 }
1102 *result = s;
1103 if (resultLen != NULL) {
1104 *resultLen = strlen(s);
1105 }
1106
1107 return 0;
1108}
1109
1110/* getprop(<property>)
1111 * Returns the named Android system property value, or "" if not set.
1112 */
1113static int
1114fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
1115 char **result, size_t *resultLen,
1116 PermissionRequestList *permissions)
1117{
1118 UNUSED(cookie);
1119 CHECK_FN();
1120 NO_PERMS(permissions);
1121
1122 if (argc != 1) {
1123 LOGE("Command %s requires exactly one argument\n", name);
1124 return 1;
1125 }
1126
1127 char value[PROPERTY_VALUE_MAX];
1128 property_get(argv[0], value, "");
1129
1130 *result = strdup(value);
1131 if (resultLen != NULL) {
1132 *resultLen = strlen(*result);
1133 }
1134
1135 return 0;
1136}
1137
1138/* file_contains(<filename>, <substring>)
1139 * Returns "true" if the file exists and contains the specified substring.
1140 */
1141static int
1142fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
1143 char **result, size_t *resultLen,
1144 PermissionRequestList *permissions)
1145{
1146 UNUSED(cookie);
1147 CHECK_FN();
1148 NO_PERMS(permissions);
1149
1150 if (argc != 2) {
1151 LOGE("Command %s requires exactly two arguments\n", name);
1152 return 1;
1153 }
1154
1155 char pathbuf[PATH_MAX];
1156 const char *root_path = argv[0];
1157 const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
1158 if (path == NULL) {
1159 LOGE("Command %s: bad path \"%s\"\n", name, root_path);
1160 return 1;
1161 }
1162
1163 if (ensure_root_path_mounted(root_path)) {
1164 LOGE("Can't mount %s\n", root_path);
1165 return 1;
1166 }
1167
1168 const char *needle = argv[1];
1169 char *haystack = (char*) load_file(path, NULL);
1170 if (haystack == NULL) {
1171 LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
1172 *result = ""; /* File not found is not an error. */
1173 } else if (strstr(haystack, needle) == NULL) {
1174 LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
1175 *result = strdup("");
1176 free(haystack);
1177 } else {
1178 *result = strdup("true");
1179 free(haystack);
1180 }
1181
1182 if (resultLen != NULL) {
1183 *resultLen = strlen(*result);
1184 }
1185 return 0;
1186}
1187
1188int
1189register_update_commands(RecoveryCommandContext *ctx)
1190{
1191 int ret;
1192
1193 ret = commandInit();
1194 if (ret < 0) return ret;
1195
1196 /*
1197 * Commands
1198 */
1199
1200 ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
1201 if (ret < 0) return ret;
1202
1203 ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
1204 if (ret < 0) return ret;
1205
1206 ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
1207 (void *)ctx);
1208 if (ret < 0) return ret;
1209
1210 ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
1211 cmd_copy_dir, (void *)ctx);
1212 if (ret < 0) return ret;
1213
1214 ret = registerCommand("run_program", CMD_ARGS_WORDS,
1215 cmd_run_program, (void *)ctx);
1216 if (ret < 0) return ret;
1217
1218 ret = registerCommand("set_perm", CMD_ARGS_WORDS,
1219 cmd_set_perm, (void *)ctx);
1220 if (ret < 0) return ret;
1221
1222 ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
1223 cmd_set_perm, (void *)ctx);
1224 if (ret < 0) return ret;
1225
1226 ret = registerCommand("show_progress", CMD_ARGS_WORDS,
1227 cmd_show_progress, (void *)ctx);
1228 if (ret < 0) return ret;
1229
1230 ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
1231 if (ret < 0) return ret;
1232
1233 ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
1234 if (ret < 0) return ret;
1235
1236 ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
1237 cmd_write_firmware_image, (void *)ctx);
1238 if (ret < 0) return ret;
1239
1240 ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
1241 cmd_write_firmware_image, (void *)ctx);
1242 if (ret < 0) return ret;
1243
1244 ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
1245 cmd_write_raw_image, (void *)ctx);
1246 if (ret < 0) return ret;
1247
1248 ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
1249 if (ret < 0) return ret;
1250
1251 ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
1252 if (ret < 0) return ret;
1253
Koushik K. Duttaa9483082010-03-07 21:47:41 -08001254 ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
1255 if (ret < 0) return ret;
1256
1257 ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
1258 if (ret < 0) return ret;
1259
Koushik K. Dutta60d7ee02010-03-07 22:16:55 -08001260 ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx);
1261 if (ret < 0) return ret;
1262
1263 ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx);
1264 if (ret < 0) return ret;
1265
Koushik K. Duttaea46fe22010-03-08 02:58:04 -08001266 ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx);
1267 if (ret < 0) return ret;
1268
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -08001269 /*
1270 * Functions
1271 */
1272
1273 ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
1274 if (ret < 0) return ret;
1275
1276 ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
1277 if (ret < 0) return ret;
1278
1279 ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
1280 if (ret < 0) return ret;
1281
1282 ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
1283 if (ret < 0) return ret;
1284
1285 ret = registerFunction("matches", fn_matches, (void *)ctx);
1286 if (ret < 0) return ret;
1287
1288 ret = registerFunction("concat", fn_concat, (void *)ctx);
1289 if (ret < 0) return ret;
1290
1291 ret = registerFunction("getprop", fn_getprop, (void *)ctx);
1292 if (ret < 0) return ret;
1293
1294 ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
1295 if (ret < 0) return ret;
1296
1297 return 0;
1298}