| Koushik K. Dutta | 6060e5c | 2010-02-11 22:27:06 -0800 | [diff] [blame] | 1 | /* | 
 | 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 |  | 
 | 44 | static 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 |  */ | 
 | 103 | static int | 
 | 104 | cmd_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 |  */ | 
 | 128 | static int | 
 | 129 | cmd_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 |  */ | 
 | 159 | static int | 
 | 160 | cmd_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 |  | 
 | 212 | typedef struct { | 
 | 213 |     int num_done; | 
 | 214 |     int num_total; | 
 | 215 | } ExtractContext; | 
 | 216 |  | 
 | 217 | static void extract_count_cb(const char *fn, void *cookie) | 
 | 218 | { | 
 | 219 |    ++((ExtractContext*) cookie)->num_total; | 
 | 220 | } | 
 | 221 |  | 
 | 222 | static 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 |  */ | 
 | 241 | static int | 
 | 242 | cmd_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 |                     ×tamp, extract_count_cb, (void *) &ctx) || | 
 | 317 |             !mzExtractRecursive(package, src_path, dst_path, | 
 | 318 |                     MZ_EXTRACT_FILES_ONLY, | 
 | 319 |                     ×tamp, 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 |  */ | 
 | 339 | static int | 
 | 340 | cmd_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. Dutta | 69b7541 | 2010-02-23 18:07:31 -0800 | [diff] [blame] | 400 |     if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) { | 
| Koushik K. Dutta | 6060e5c | 2010-02-11 22:27:06 -0800 | [diff] [blame] | 401 |         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 |  */ | 
 | 415 | static int | 
 | 416 | cmd_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 |  */ | 
 | 469 | static int | 
 | 470 | cmd_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 |  */ | 
 | 507 | static int | 
 | 508 | cmd_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 |  | 
 | 538 | struct FirmwareContext { | 
 | 539 |     size_t total_bytes, done_bytes; | 
 | 540 |     char *data; | 
 | 541 | }; | 
 | 542 |  | 
 | 543 | static 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 |  */ | 
 | 561 | static int | 
 | 562 | cmd_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 |  | 
 | 629 | static 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 |  */ | 
 | 641 | static int | 
 | 642 | cmd_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 |  */ | 
 | 734 | static int | 
 | 735 | cmd_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 |  */ | 
 | 750 | static int | 
 | 751 | cmd_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. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 762 | static int | 
 | 763 | cmd_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; | 
| Koushik K. Dutta | 7e0a72c | 2010-03-15 00:54:34 -0700 | [diff] [blame] | 770 |     char backup_path[PATH_MAX]; | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 771 |     switch(argc) | 
 | 772 |     { | 
 | 773 |         case 0: | 
| Koushik K. Dutta | 7e0a72c | 2010-03-15 00:54:34 -0700 | [diff] [blame] | 774 |             { | 
| Koushik K. Dutta | 6a26e7c | 2010-03-27 15:26:11 -0700 | [diff] [blame] | 775 |                 char backup_path[PATH_MAX]; | 
 | 776 |                 time_t t = time(NULL); | 
 | 777 |                 struct tm *tmp = localtime(&t); | 
 | 778 |                 if (tmp == NULL) | 
 | 779 |                 { | 
 | 780 |                     struct timeval tp; | 
 | 781 |                     gettimeofday(&tp, NULL); | 
 | 782 |                     sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec); | 
 | 783 |                 } | 
 | 784 |                 else | 
 | 785 |                 { | 
 | 786 |                     strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp); | 
 | 787 |                 } | 
| Koushik K. Dutta | 7e0a72c | 2010-03-15 00:54:34 -0700 | [diff] [blame] | 788 |                 backup_name = backup_path; | 
 | 789 |             } | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 790 |             break; | 
 | 791 |         case 1: | 
 | 792 |             backup_name = argv[0]; | 
 | 793 |             break; | 
 | 794 |         default: | 
 | 795 |             LOGE("Command %s requires zero or one argument\n", name); | 
 | 796 |             return 1; | 
 | 797 |     } | 
 | 798 |  | 
| Koushik K. Dutta | ee57bbc | 2010-03-12 23:21:12 -0800 | [diff] [blame] | 799 |     return nandroid_backup(backup_name); | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 800 | } | 
 | 801 |  | 
 | 802 | static int | 
 | 803 | cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[], | 
 | 804 |         PermissionRequestList *permissions) | 
 | 805 | { | 
 | 806 |     UNUSED(cookie); | 
 | 807 |     CHECK_WORDS(); | 
 | 808 |  | 
| Koushik K. Dutta | 6923cc3 | 2010-03-29 14:46:00 -0700 | [diff] [blame^] | 809 |     int restoreboot = 1; | 
 | 810 |     int restoresystem = 1; | 
 | 811 |     int restoredata = 1; | 
 | 812 |     int restorecache = 1; | 
 | 813 |     int i; | 
 | 814 |     for (i = 0; i < argc; i++) | 
 | 815 |     { | 
 | 816 |         if (strcmp(argv[i], "noboot") == 0) | 
 | 817 |             restoreboot = 0; | 
 | 818 |         else if (strcmp(argv[i], "nosystem") == 0) | 
 | 819 |             restoresystem = 0; | 
 | 820 |         else if (strcmp(argv[i], "nodata") == 0) | 
 | 821 |             restoredata = 0; | 
 | 822 |         else if (strcmp(argv[i], "nocache") == 0) | 
 | 823 |             restorecache = 0; | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 824 |     } | 
 | 825 |  | 
| Koushik K. Dutta | 6923cc3 | 2010-03-29 14:46:00 -0700 | [diff] [blame^] | 826 |     return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache); | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 827 | } | 
 | 828 |  | 
| Koushik K. Dutta | 60d7ee0 | 2010-03-07 22:16:55 -0800 | [diff] [blame] | 829 | static int | 
 | 830 | cmd_sleep(const char *name, void *cookie, int argc, const char *argv[], | 
 | 831 |             PermissionRequestList *permissions) | 
 | 832 | { | 
 | 833 |     UNUSED(cookie); | 
 | 834 |     CHECK_WORDS(); | 
 | 835 |  | 
 | 836 |     if (argc != 1) { | 
 | 837 |         LOGE("Command %s requires exactly one argument\n", name); | 
 | 838 |         return 1; | 
 | 839 |     } | 
 | 840 |      | 
 | 841 |     int seconds = atoi(argv[0]); | 
 | 842 |     sleep(seconds); | 
 | 843 |  | 
 | 844 |     return 0; | 
 | 845 | } | 
 | 846 |  | 
 | 847 | static int | 
 | 848 | cmd_print(const char *name, void *cookie, int argc, const char *argv[], | 
 | 849 |             PermissionRequestList *permissions) | 
 | 850 | { | 
 | 851 |     UNUSED(cookie); | 
 | 852 |     CHECK_WORDS(); | 
 | 853 |      | 
 | 854 |     char message[1024]; | 
 | 855 |     message[0] = NULL; | 
 | 856 |     int i; | 
 | 857 |     for (i = 0; i < argc; i++) | 
 | 858 |     { | 
 | 859 |         strcat(message, argv[i]); | 
 | 860 |         strcat(message, " "); | 
 | 861 |     } | 
 | 862 |     strcat(message, "\n"); | 
 | 863 |      | 
 | 864 |     ui_print(message); | 
 | 865 |     return 0; | 
 | 866 | } | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 867 |  | 
| Koushik K. Dutta | ea46fe2 | 2010-03-08 02:58:04 -0800 | [diff] [blame] | 868 | static int | 
 | 869 | cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[], | 
 | 870 |             PermissionRequestList *permissions) | 
 | 871 | { | 
 | 872 |     UNUSED(cookie); | 
 | 873 |     CHECK_WORDS(); | 
 | 874 |      | 
 | 875 |     if (argc != 1) { | 
 | 876 |         LOGE("Command %s requires exactly one argument\n", name); | 
 | 877 |         return 1; | 
 | 878 |     } | 
 | 879 |  | 
 | 880 |     return install_zip(argv[0]); | 
 | 881 | } | 
 | 882 |  | 
| Koushik K. Dutta | 6060e5c | 2010-02-11 22:27:06 -0800 | [diff] [blame] | 883 | /* | 
 | 884 |  * Function definitions | 
 | 885 |  */ | 
 | 886 |  | 
 | 887 | /* compatible_with(<version>) | 
 | 888 |  * | 
 | 889 |  * Returns "true" if this version of the script parser and command | 
 | 890 |  * set supports the named version. | 
 | 891 |  */ | 
 | 892 | static int | 
 | 893 | fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[], | 
 | 894 |         char **result, size_t *resultLen, | 
 | 895 |         PermissionRequestList *permissions) | 
 | 896 | { | 
 | 897 |     UNUSED(name); | 
 | 898 |     UNUSED(cookie); | 
 | 899 |     CHECK_FN(); | 
 | 900 |     NO_PERMS(permissions); | 
 | 901 |  | 
 | 902 |     if (argc != 1) { | 
 | 903 |         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | 
 | 904 |                 name, argc); | 
 | 905 |         return 1; | 
 | 906 |     } | 
 | 907 |  | 
 | 908 |     if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) { | 
 | 909 |         *result = strdup("true"); | 
 | 910 |     } else { | 
 | 911 |         *result = strdup(""); | 
 | 912 |     } | 
 | 913 |     if (resultLen != NULL) { | 
 | 914 |         *resultLen = strlen(*result); | 
 | 915 |     } | 
 | 916 |     return 0; | 
 | 917 | } | 
 | 918 |  | 
 | 919 | /* update_forced() | 
 | 920 |  * | 
 | 921 |  * Returns "true" if some system setting has determined that | 
 | 922 |  * the update should happen no matter what. | 
 | 923 |  */ | 
 | 924 | static int | 
 | 925 | fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], | 
 | 926 |         char **result, size_t *resultLen, | 
 | 927 |         PermissionRequestList *permissions) | 
 | 928 | { | 
 | 929 |     UNUSED(name); | 
 | 930 |     UNUSED(cookie); | 
 | 931 |     CHECK_FN(); | 
 | 932 |     NO_PERMS(permissions); | 
 | 933 |  | 
 | 934 |     if (argc != 0) { | 
 | 935 |         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | 
 | 936 |                 name, argc); | 
 | 937 |         return 1; | 
 | 938 |     } | 
 | 939 |  | 
 | 940 |     //xxx check some global or property | 
 | 941 |     bool force = true; | 
 | 942 |     if (force) { | 
 | 943 |         *result = strdup("true"); | 
 | 944 |     } else { | 
 | 945 |         *result = strdup(""); | 
 | 946 |     } | 
 | 947 |     if (resultLen != NULL) { | 
 | 948 |         *resultLen = strlen(*result); | 
 | 949 |     } | 
 | 950 |  | 
 | 951 |     return 0; | 
 | 952 | } | 
 | 953 |  | 
 | 954 | /* get_mark(<resource>) | 
 | 955 |  * | 
 | 956 |  * Returns the current mark associated with the provided resource. | 
 | 957 |  */ | 
 | 958 | static int | 
 | 959 | fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], | 
 | 960 |         char **result, size_t *resultLen, | 
 | 961 |         PermissionRequestList *permissions) | 
 | 962 | { | 
 | 963 |     UNUSED(name); | 
 | 964 |     UNUSED(cookie); | 
 | 965 |     CHECK_FN(); | 
 | 966 |     NO_PERMS(permissions); | 
 | 967 |  | 
 | 968 |     if (argc != 1) { | 
 | 969 |         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | 
 | 970 |                 name, argc); | 
 | 971 |         return 1; | 
 | 972 |     } | 
 | 973 |  | 
 | 974 |     //xxx look up the value | 
 | 975 |     *result = strdup(""); | 
 | 976 |     if (resultLen != NULL) { | 
 | 977 |         *resultLen = strlen(*result); | 
 | 978 |     } | 
 | 979 |  | 
 | 980 |     return 0; | 
 | 981 | } | 
 | 982 |  | 
 | 983 | /* hash_dir(<path-to-directory>) | 
 | 984 |  */ | 
 | 985 | static int | 
 | 986 | fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], | 
 | 987 |         char **result, size_t *resultLen, | 
 | 988 |         PermissionRequestList *permissions) | 
 | 989 | { | 
 | 990 |     int ret = -1; | 
 | 991 |  | 
 | 992 |     UNUSED(name); | 
 | 993 |     UNUSED(cookie); | 
 | 994 |     CHECK_FN(); | 
 | 995 |  | 
 | 996 |     const char *dir; | 
 | 997 |     if (argc != 1) { | 
 | 998 |         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | 
 | 999 |                 name, argc); | 
 | 1000 |         return 1; | 
 | 1001 |     } else { | 
 | 1002 |         dir = argv[0]; | 
 | 1003 |     } | 
 | 1004 |  | 
 | 1005 |     if (permissions != NULL) { | 
 | 1006 |         if (dir == NULL) { | 
 | 1007 |             /* The argument is the result of another function. | 
 | 1008 |              * Assume the worst case, where the function returns | 
 | 1009 |              * the root. | 
 | 1010 |              */ | 
 | 1011 |             dir = "/"; | 
 | 1012 |         } | 
 | 1013 |         ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); | 
 | 1014 |     } else { | 
 | 1015 | //xxx build and return the string | 
 | 1016 |         *result = strdup("hashvalue"); | 
 | 1017 |         if (resultLen != NULL) { | 
 | 1018 |             *resultLen = strlen(*result); | 
 | 1019 |         } | 
 | 1020 |         ret = 0; | 
 | 1021 |     } | 
 | 1022 |  | 
 | 1023 |     return ret; | 
 | 1024 | } | 
 | 1025 |  | 
 | 1026 | /* matches(<str>, <str1> [, <strN>...]) | 
 | 1027 |  * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>, | 
 | 1028 |  * otherwise returns "". | 
 | 1029 |  * | 
 | 1030 |  * E.g., assert matches(hash_dir("/path"), "hash1", "hash2") | 
 | 1031 |  */ | 
 | 1032 | static int | 
 | 1033 | fn_matches(const char *name, void *cookie, int argc, const char *argv[], | 
 | 1034 |         char **result, size_t *resultLen, | 
 | 1035 |         PermissionRequestList *permissions) | 
 | 1036 | { | 
 | 1037 |     UNUSED(name); | 
 | 1038 |     UNUSED(cookie); | 
 | 1039 |     CHECK_FN(); | 
 | 1040 |     NO_PERMS(permissions); | 
 | 1041 |  | 
 | 1042 |     if (argc < 2) { | 
 | 1043 |         fprintf(stderr, "%s: not enough arguments (%d < 2)\n", | 
 | 1044 |                 name, argc); | 
 | 1045 |         return 1; | 
 | 1046 |     } | 
 | 1047 |  | 
 | 1048 |     int i; | 
 | 1049 |     for (i = 1; i < argc; i++) { | 
 | 1050 |         if (strcmp(argv[0], argv[i]) == 0) { | 
 | 1051 |             *result = strdup(argv[0]); | 
 | 1052 |             if (resultLen != NULL) { | 
 | 1053 |                 *resultLen = strlen(*result); | 
 | 1054 |             } | 
 | 1055 |             return 0; | 
 | 1056 |         } | 
 | 1057 |     } | 
 | 1058 |  | 
 | 1059 |     *result = strdup(""); | 
 | 1060 |     if (resultLen != NULL) { | 
 | 1061 |         *resultLen = 1; | 
 | 1062 |     } | 
 | 1063 |     return 0; | 
 | 1064 | } | 
 | 1065 |  | 
 | 1066 | /* concat(<str>, <str1> [, <strN>...]) | 
 | 1067 |  * Returns the concatenation of all strings. | 
 | 1068 |  */ | 
 | 1069 | static int | 
 | 1070 | fn_concat(const char *name, void *cookie, int argc, const char *argv[], | 
 | 1071 |         char **result, size_t *resultLen, | 
 | 1072 |         PermissionRequestList *permissions) | 
 | 1073 | { | 
 | 1074 |     UNUSED(name); | 
 | 1075 |     UNUSED(cookie); | 
 | 1076 |     CHECK_FN(); | 
 | 1077 |     NO_PERMS(permissions); | 
 | 1078 |  | 
 | 1079 |     size_t totalLen = 0; | 
 | 1080 |     int i; | 
 | 1081 |     for (i = 0; i < argc; i++) { | 
 | 1082 |         totalLen += strlen(argv[i]); | 
 | 1083 |     } | 
 | 1084 |  | 
 | 1085 |     char *s = (char *)malloc(totalLen + 1); | 
 | 1086 |     if (s == NULL) { | 
 | 1087 |         return -1; | 
 | 1088 |     } | 
 | 1089 |     s[totalLen] = '\0'; | 
 | 1090 |     for (i = 0; i < argc; i++) { | 
 | 1091 |         //TODO: keep track of the end to avoid walking the string each time | 
 | 1092 |         strcat(s, argv[i]); | 
 | 1093 |     } | 
 | 1094 |     *result = s; | 
 | 1095 |     if (resultLen != NULL) { | 
 | 1096 |         *resultLen = strlen(s); | 
 | 1097 |     } | 
 | 1098 |  | 
 | 1099 |     return 0; | 
 | 1100 | } | 
 | 1101 |  | 
 | 1102 | /* getprop(<property>) | 
 | 1103 |  * Returns the named Android system property value, or "" if not set. | 
 | 1104 |  */ | 
 | 1105 | static int | 
 | 1106 | fn_getprop(const char *name, void *cookie, int argc, const char *argv[], | 
 | 1107 |         char **result, size_t *resultLen, | 
 | 1108 |         PermissionRequestList *permissions) | 
 | 1109 | { | 
 | 1110 |     UNUSED(cookie); | 
 | 1111 |     CHECK_FN(); | 
 | 1112 |     NO_PERMS(permissions); | 
 | 1113 |  | 
 | 1114 |     if (argc != 1) { | 
 | 1115 |         LOGE("Command %s requires exactly one argument\n", name); | 
 | 1116 |         return 1; | 
 | 1117 |     } | 
 | 1118 |  | 
 | 1119 |     char value[PROPERTY_VALUE_MAX]; | 
 | 1120 |     property_get(argv[0], value, ""); | 
 | 1121 |  | 
 | 1122 |     *result = strdup(value); | 
 | 1123 |     if (resultLen != NULL) { | 
 | 1124 |         *resultLen = strlen(*result); | 
 | 1125 |     } | 
 | 1126 |  | 
 | 1127 |     return 0; | 
 | 1128 | } | 
 | 1129 |  | 
 | 1130 | /* file_contains(<filename>, <substring>) | 
 | 1131 |  * Returns "true" if the file exists and contains the specified substring. | 
 | 1132 |  */ | 
 | 1133 | static int | 
 | 1134 | fn_file_contains(const char *name, void *cookie, int argc, const char *argv[], | 
 | 1135 |         char **result, size_t *resultLen, | 
 | 1136 |         PermissionRequestList *permissions) | 
 | 1137 | { | 
 | 1138 |     UNUSED(cookie); | 
 | 1139 |     CHECK_FN(); | 
 | 1140 |     NO_PERMS(permissions); | 
 | 1141 |  | 
 | 1142 |     if (argc != 2) { | 
 | 1143 |         LOGE("Command %s requires exactly two arguments\n", name); | 
 | 1144 |         return 1; | 
 | 1145 |     } | 
 | 1146 |  | 
 | 1147 |     char pathbuf[PATH_MAX]; | 
 | 1148 |     const char *root_path = argv[0]; | 
 | 1149 |     const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf)); | 
 | 1150 |     if (path == NULL) { | 
 | 1151 |         LOGE("Command %s: bad path \"%s\"\n", name, root_path); | 
 | 1152 |         return 1; | 
 | 1153 |     } | 
 | 1154 |  | 
 | 1155 |     if (ensure_root_path_mounted(root_path)) { | 
 | 1156 |         LOGE("Can't mount %s\n", root_path); | 
 | 1157 |         return 1; | 
 | 1158 |     } | 
 | 1159 |  | 
 | 1160 |     const char *needle = argv[1]; | 
 | 1161 |     char *haystack = (char*) load_file(path, NULL); | 
 | 1162 |     if (haystack == NULL) { | 
 | 1163 |         LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno)); | 
 | 1164 |         *result = "";  /* File not found is not an error. */ | 
 | 1165 |     } else if (strstr(haystack, needle) == NULL) { | 
 | 1166 |         LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path); | 
 | 1167 |         *result = strdup(""); | 
 | 1168 |         free(haystack); | 
 | 1169 |     } else { | 
 | 1170 |         *result = strdup("true"); | 
 | 1171 |         free(haystack); | 
 | 1172 |     } | 
 | 1173 |  | 
 | 1174 |     if (resultLen != NULL) { | 
 | 1175 |         *resultLen = strlen(*result); | 
 | 1176 |     } | 
 | 1177 |     return 0; | 
 | 1178 | } | 
 | 1179 |  | 
 | 1180 | int | 
 | 1181 | register_update_commands(RecoveryCommandContext *ctx) | 
 | 1182 | { | 
 | 1183 |     int ret; | 
 | 1184 |  | 
 | 1185 |     ret = commandInit(); | 
 | 1186 |     if (ret < 0) return ret; | 
 | 1187 |  | 
 | 1188 |     /* | 
 | 1189 |      * Commands | 
 | 1190 |      */ | 
 | 1191 |  | 
 | 1192 |     ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx); | 
 | 1193 |     if (ret < 0) return ret; | 
 | 1194 |  | 
 | 1195 |     ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx); | 
 | 1196 |     if (ret < 0) return ret; | 
 | 1197 |  | 
 | 1198 |     ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete, | 
 | 1199 |             (void *)ctx); | 
 | 1200 |     if (ret < 0) return ret; | 
 | 1201 |  | 
 | 1202 |     ret = registerCommand("copy_dir", CMD_ARGS_WORDS, | 
 | 1203 |             cmd_copy_dir, (void *)ctx); | 
 | 1204 |     if (ret < 0) return ret; | 
 | 1205 |  | 
 | 1206 |     ret = registerCommand("run_program", CMD_ARGS_WORDS, | 
 | 1207 |             cmd_run_program, (void *)ctx); | 
 | 1208 |     if (ret < 0) return ret; | 
 | 1209 |  | 
 | 1210 |     ret = registerCommand("set_perm", CMD_ARGS_WORDS, | 
 | 1211 |             cmd_set_perm, (void *)ctx); | 
 | 1212 |     if (ret < 0) return ret; | 
 | 1213 |  | 
 | 1214 |     ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS, | 
 | 1215 |             cmd_set_perm, (void *)ctx); | 
 | 1216 |     if (ret < 0) return ret; | 
 | 1217 |  | 
 | 1218 |     ret = registerCommand("show_progress", CMD_ARGS_WORDS, | 
 | 1219 |             cmd_show_progress, (void *)ctx); | 
 | 1220 |     if (ret < 0) return ret; | 
 | 1221 |  | 
 | 1222 |     ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx); | 
 | 1223 |     if (ret < 0) return ret; | 
 | 1224 |  | 
 | 1225 |     ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx); | 
 | 1226 |     if (ret < 0) return ret; | 
 | 1227 |  | 
 | 1228 |     ret = registerCommand("write_radio_image", CMD_ARGS_WORDS, | 
 | 1229 |             cmd_write_firmware_image, (void *)ctx); | 
 | 1230 |     if (ret < 0) return ret; | 
 | 1231 |  | 
 | 1232 |     ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS, | 
 | 1233 |             cmd_write_firmware_image, (void *)ctx); | 
 | 1234 |     if (ret < 0) return ret; | 
 | 1235 |  | 
 | 1236 |     ret = registerCommand("write_raw_image", CMD_ARGS_WORDS, | 
 | 1237 |             cmd_write_raw_image, (void *)ctx); | 
 | 1238 |     if (ret < 0) return ret; | 
 | 1239 |  | 
 | 1240 |     ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx); | 
 | 1241 |     if (ret < 0) return ret; | 
 | 1242 |  | 
 | 1243 |     ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx); | 
 | 1244 |     if (ret < 0) return ret; | 
 | 1245 |  | 
| Koushik K. Dutta | a948308 | 2010-03-07 21:47:41 -0800 | [diff] [blame] | 1246 |     ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx); | 
 | 1247 |     if (ret < 0) return ret; | 
 | 1248 |  | 
 | 1249 |     ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx); | 
 | 1250 |     if (ret < 0) return ret; | 
 | 1251 |  | 
| Koushik K. Dutta | 60d7ee0 | 2010-03-07 22:16:55 -0800 | [diff] [blame] | 1252 |     ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx); | 
 | 1253 |     if (ret < 0) return ret; | 
 | 1254 |  | 
 | 1255 |     ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx); | 
 | 1256 |     if (ret < 0) return ret; | 
 | 1257 |  | 
| Koushik K. Dutta | ea46fe2 | 2010-03-08 02:58:04 -0800 | [diff] [blame] | 1258 |     ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx); | 
 | 1259 |     if (ret < 0) return ret; | 
 | 1260 |  | 
| Koushik K. Dutta | 6060e5c | 2010-02-11 22:27:06 -0800 | [diff] [blame] | 1261 |     /* | 
 | 1262 |      * Functions | 
 | 1263 |      */ | 
 | 1264 |  | 
 | 1265 |     ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx); | 
 | 1266 |     if (ret < 0) return ret; | 
 | 1267 |  | 
 | 1268 |     ret = registerFunction("update_forced", fn_update_forced, (void *)ctx); | 
 | 1269 |     if (ret < 0) return ret; | 
 | 1270 |  | 
 | 1271 |     ret = registerFunction("get_mark", fn_get_mark, (void *)ctx); | 
 | 1272 |     if (ret < 0) return ret; | 
 | 1273 |  | 
 | 1274 |     ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx); | 
 | 1275 |     if (ret < 0) return ret; | 
 | 1276 |  | 
 | 1277 |     ret = registerFunction("matches", fn_matches, (void *)ctx); | 
 | 1278 |     if (ret < 0) return ret; | 
 | 1279 |  | 
 | 1280 |     ret = registerFunction("concat", fn_concat, (void *)ctx); | 
 | 1281 |     if (ret < 0) return ret; | 
 | 1282 |  | 
 | 1283 |     ret = registerFunction("getprop", fn_getprop, (void *)ctx); | 
 | 1284 |     if (ret < 0) return ret; | 
 | 1285 |  | 
 | 1286 |     ret = registerFunction("file_contains", fn_file_contains, (void *)ctx); | 
 | 1287 |     if (ret < 0) return ret; | 
 | 1288 |  | 
 | 1289 |     return 0; | 
 | 1290 | } |