1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <fcntl.h> 19#include <net/if.h> 20#include <stdio.h> 21#include <stdlib.h> 22#include <string.h> 23#include <sys/socket.h> 24#include <sys/mount.h> 25#include <sys/resource.h> 26#include <sys/time.h> 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <sys/wait.h> 30#include <unistd.h> 31#include <linux/loop.h> 32#include <ext4_crypt_init_extensions.h> 33 34#include <selinux/selinux.h> 35#include <selinux/label.h> 36 37#include <fs_mgr.h> 38#include <base/stringprintf.h> 39#include <cutils/partition_utils.h> 40#include <cutils/android_reboot.h> 41#include <private/android_filesystem_config.h> 42 43#include "init.h" 44#include "keywords.h" 45#include "property_service.h" 46#include "devices.h" 47#include "init_parser.h" 48#include "util.h" 49#include "log.h" 50 51#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW 52 53int add_environment(const char *name, const char *value); 54 55// System call provided by bionic but not in any header file. 56extern "C" int init_module(void *, unsigned long, const char *); 57 58static int insmod(const char *filename, char *options) 59{ 60 char filename_val[PROP_VALUE_MAX]; 61 if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) { 62 ERROR("insmod: cannot expand '%s'\n", filename); 63 return -EINVAL; 64 } 65 66 std::string module; 67 if (!read_file(filename_val, &module)) { 68 return -1; 69 } 70 71 // TODO: use finit_module for >= 3.8 kernels. 72 return init_module(&module[0], module.size(), options); 73} 74 75static int __ifupdown(const char *interface, int up) 76{ 77 struct ifreq ifr; 78 int s, ret; 79 80 strlcpy(ifr.ifr_name, interface, IFNAMSIZ); 81 82 s = socket(AF_INET, SOCK_DGRAM, 0); 83 if (s < 0) 84 return -1; 85 86 ret = ioctl(s, SIOCGIFFLAGS, &ifr); 87 if (ret < 0) { 88 goto done; 89 } 90 91 if (up) 92 ifr.ifr_flags |= IFF_UP; 93 else 94 ifr.ifr_flags &= ~IFF_UP; 95 96 ret = ioctl(s, SIOCSIFFLAGS, &ifr); 97 98done: 99 close(s); 100 return ret; 101} 102 103static void service_start_if_not_disabled(struct service *svc) 104{ 105 if (!(svc->flags & SVC_DISABLED)) { 106 service_start(svc, NULL); 107 } else { 108 svc->flags |= SVC_DISABLED_START; 109 } 110} 111 112int do_class_start(int nargs, char **args) 113{ 114 /* Starting a class does not start services 115 * which are explicitly disabled. They must 116 * be started individually. 117 */ 118 service_for_each_class(args[1], service_start_if_not_disabled); 119 return 0; 120} 121 122int do_class_stop(int nargs, char **args) 123{ 124 service_for_each_class(args[1], service_stop); 125 return 0; 126} 127 128int do_class_reset(int nargs, char **args) 129{ 130 service_for_each_class(args[1], service_reset); 131 return 0; 132} 133 134int do_domainname(int nargs, char **args) 135{ 136 return write_file("/proc/sys/kernel/domainname", args[1]); 137} 138 139int do_enable(int nargs, char **args) 140{ 141 struct service *svc; 142 svc = service_find_by_name(args[1]); 143 if (svc) { 144 svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED); 145 if (svc->flags & SVC_DISABLED_START) { 146 service_start(svc, NULL); 147 } 148 } else { 149 return -1; 150 } 151 return 0; 152} 153 154int do_exec(int nargs, char** args) { 155 service* svc = make_exec_oneshot_service(nargs, args); 156 if (svc == NULL) { 157 return -1; 158 } 159 service_start(svc, NULL); 160 return 0; 161} 162 163int do_export(int nargs, char **args) 164{ 165 return add_environment(args[1], args[2]); 166} 167 168int do_hostname(int nargs, char **args) 169{ 170 return write_file("/proc/sys/kernel/hostname", args[1]); 171} 172 173int do_ifup(int nargs, char **args) 174{ 175 return __ifupdown(args[1], 1); 176} 177 178 179static int do_insmod_inner(int nargs, char **args, int opt_len) 180{ 181 char options[opt_len + 1]; 182 int i; 183 184 options[0] = '\0'; 185 if (nargs > 2) { 186 strcpy(options, args[2]); 187 for (i = 3; i < nargs; ++i) { 188 strcat(options, " "); 189 strcat(options, args[i]); 190 } 191 } 192 193 return insmod(args[1], options); 194} 195 196int do_insmod(int nargs, char **args) 197{ 198 int i; 199 int size = 0; 200 201 if (nargs > 2) { 202 for (i = 2; i < nargs; ++i) 203 size += strlen(args[i]) + 1; 204 } 205 206 return do_insmod_inner(nargs, args, size); 207} 208 209int do_mkdir(int nargs, char **args) 210{ 211 mode_t mode = 0755; 212 int ret; 213 214 /* mkdir <path> [mode] [owner] [group] */ 215 216 if (nargs >= 3) { 217 mode = strtoul(args[2], 0, 8); 218 } 219 220 ret = make_dir(args[1], mode); 221 /* chmod in case the directory already exists */ 222 if (ret == -1 && errno == EEXIST) { 223 ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW); 224 } 225 if (ret == -1) { 226 return -errno; 227 } 228 229 if (nargs >= 4) { 230 uid_t uid = decode_uid(args[3]); 231 gid_t gid = -1; 232 233 if (nargs == 5) { 234 gid = decode_uid(args[4]); 235 } 236 237 if (lchown(args[1], uid, gid) == -1) { 238 return -errno; 239 } 240 241 /* chown may have cleared S_ISUID and S_ISGID, chmod again */ 242 if (mode & (S_ISUID | S_ISGID)) { 243 ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW); 244 if (ret == -1) { 245 return -errno; 246 } 247 } 248 } 249 250 return e4crypt_set_directory_policy(args[1]); 251} 252 253static struct { 254 const char *name; 255 unsigned flag; 256} mount_flags[] = { 257 { "noatime", MS_NOATIME }, 258 { "noexec", MS_NOEXEC }, 259 { "nosuid", MS_NOSUID }, 260 { "nodev", MS_NODEV }, 261 { "nodiratime", MS_NODIRATIME }, 262 { "ro", MS_RDONLY }, 263 { "rw", 0 }, 264 { "remount", MS_REMOUNT }, 265 { "bind", MS_BIND }, 266 { "rec", MS_REC }, 267 { "unbindable", MS_UNBINDABLE }, 268 { "private", MS_PRIVATE }, 269 { "slave", MS_SLAVE }, 270 { "shared", MS_SHARED }, 271 { "defaults", 0 }, 272 { 0, 0 }, 273}; 274 275#define DATA_MNT_POINT "/data" 276 277/* mount <type> <device> <path> <flags ...> <options> */ 278int do_mount(int nargs, char **args) 279{ 280 char tmp[64]; 281 char *source, *target, *system; 282 char *options = NULL; 283 unsigned flags = 0; 284 int n, i; 285 int wait = 0; 286 287 for (n = 4; n < nargs; n++) { 288 for (i = 0; mount_flags[i].name; i++) { 289 if (!strcmp(args[n], mount_flags[i].name)) { 290 flags |= mount_flags[i].flag; 291 break; 292 } 293 } 294 295 if (!mount_flags[i].name) { 296 if (!strcmp(args[n], "wait")) 297 wait = 1; 298 /* if our last argument isn't a flag, wolf it up as an option string */ 299 else if (n + 1 == nargs) 300 options = args[n]; 301 } 302 } 303 304 system = args[1]; 305 source = args[2]; 306 target = args[3]; 307 308 if (!strncmp(source, "mtd@", 4)) { 309 n = mtd_name_to_number(source + 4); 310 if (n < 0) { 311 return -1; 312 } 313 314 snprintf(tmp, sizeof(tmp), "/dev/block/mtdblock%d", n); 315 316 if (wait) 317 wait_for_file(tmp, COMMAND_RETRY_TIMEOUT); 318 if (mount(tmp, target, system, flags, options) < 0) { 319 return -1; 320 } 321 322 goto exit_success; 323 } else if (!strncmp(source, "loop@", 5)) { 324 int mode, loop, fd; 325 struct loop_info info; 326 327 mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR; 328 fd = open(source + 5, mode | O_CLOEXEC); 329 if (fd < 0) { 330 return -1; 331 } 332 333 for (n = 0; ; n++) { 334 snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n); 335 loop = open(tmp, mode | O_CLOEXEC); 336 if (loop < 0) { 337 close(fd); 338 return -1; 339 } 340 341 /* if it is a blank loop device */ 342 if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) { 343 /* if it becomes our loop device */ 344 if (ioctl(loop, LOOP_SET_FD, fd) >= 0) { 345 close(fd); 346 347 if (mount(tmp, target, system, flags, options) < 0) { 348 ioctl(loop, LOOP_CLR_FD, 0); 349 close(loop); 350 return -1; 351 } 352 353 close(loop); 354 goto exit_success; 355 } 356 } 357 358 close(loop); 359 } 360 361 close(fd); 362 ERROR("out of loopback devices"); 363 return -1; 364 } else { 365 if (wait) 366 wait_for_file(source, COMMAND_RETRY_TIMEOUT); 367 if (mount(source, target, system, flags, options) < 0) { 368 return -1; 369 } 370 371 } 372 373exit_success: 374 return 0; 375 376} 377 378static int wipe_data_via_recovery() 379{ 380 mkdir("/cache/recovery", 0700); 381 int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600); 382 if (fd >= 0) { 383 write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1); 384 write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1); 385 close(fd); 386 } else { 387 ERROR("could not open /cache/recovery/command\n"); 388 return -1; 389 } 390 android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); 391 while (1) { pause(); } // never reached 392} 393 394/* 395 * This function might request a reboot, in which case it will 396 * not return. 397 */ 398int do_mount_all(int nargs, char **args) 399{ 400 pid_t pid; 401 int ret = -1; 402 int child_ret = -1; 403 int status; 404 struct fstab *fstab; 405 406 if (nargs != 2) { 407 return -1; 408 } 409 410 /* 411 * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and 412 * do the call in the child to provide protection to the main init 413 * process if anything goes wrong (crash or memory leak), and wait for 414 * the child to finish in the parent. 415 */ 416 pid = fork(); 417 if (pid > 0) { 418 /* Parent. Wait for the child to return */ 419 int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); 420 if (wp_ret < 0) { 421 /* Unexpected error code. We will continue anyway. */ 422 NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno)); 423 } 424 425 if (WIFEXITED(status)) { 426 ret = WEXITSTATUS(status); 427 } else { 428 ret = -1; 429 } 430 } else if (pid == 0) { 431 /* child, call fs_mgr_mount_all() */ 432 klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */ 433 fstab = fs_mgr_read_fstab(args[1]); 434 child_ret = fs_mgr_mount_all(fstab); 435 fs_mgr_free_fstab(fstab); 436 if (child_ret == -1) { 437 ERROR("fs_mgr_mount_all returned an error\n"); 438 } 439 _exit(child_ret); 440 } else { 441 /* fork failed, return an error */ 442 return -1; 443 } 444 445 if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) { 446 property_set("vold.decrypt", "trigger_encryption"); 447 } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) { 448 property_set("ro.crypto.state", "encrypted"); 449 property_set("ro.crypto.type", "block"); 450 property_set("vold.decrypt", "trigger_default_encryption"); 451 } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { 452 property_set("ro.crypto.state", "unencrypted"); 453 /* If fs_mgr determined this is an unencrypted device, then trigger 454 * that action. 455 */ 456 action_for_each_trigger("nonencrypted", action_add_queue_tail); 457 } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) { 458 /* Setup a wipe via recovery, and reboot into recovery */ 459 ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n"); 460 ret = wipe_data_via_recovery(); 461 /* If reboot worked, there is no return. */ 462 } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) { 463 if (e4crypt_install_keyring()) { 464 return -1; 465 } 466 property_set("ro.crypto.state", "encrypted"); 467 property_set("ro.crypto.type", "file"); 468 469 // Although encrypted, we have device key, so we do not need to 470 // do anything different from the nonencrypted case. 471 action_for_each_trigger("nonencrypted", action_add_queue_tail); 472 } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) { 473 if (e4crypt_install_keyring()) { 474 return -1; 475 } 476 property_set("ro.crypto.state", "encrypted"); 477 property_set("ro.crypto.type", "file"); 478 property_set("vold.decrypt", "trigger_restart_min_framework"); 479 } else if (ret > 0) { 480 ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret); 481 } 482 /* else ... < 0: error */ 483 484 return ret; 485} 486 487int do_swapon_all(int nargs, char **args) 488{ 489 struct fstab *fstab; 490 int ret; 491 492 fstab = fs_mgr_read_fstab(args[1]); 493 ret = fs_mgr_swapon_all(fstab); 494 fs_mgr_free_fstab(fstab); 495 496 return ret; 497} 498 499int do_setprop(int nargs, char **args) 500{ 501 const char *name = args[1]; 502 const char *value = args[2]; 503 char prop_val[PROP_VALUE_MAX]; 504 int ret; 505 506 ret = expand_props(prop_val, value, sizeof(prop_val)); 507 if (ret) { 508 ERROR("cannot expand '%s' while assigning to '%s'\n", value, name); 509 return -EINVAL; 510 } 511 property_set(name, prop_val); 512 return 0; 513} 514 515int do_setrlimit(int nargs, char **args) 516{ 517 struct rlimit limit; 518 int resource; 519 resource = atoi(args[1]); 520 limit.rlim_cur = atoi(args[2]); 521 limit.rlim_max = atoi(args[3]); 522 return setrlimit(resource, &limit); 523} 524 525int do_start(int nargs, char **args) 526{ 527 struct service *svc; 528 svc = service_find_by_name(args[1]); 529 if (svc) { 530 service_start(svc, NULL); 531 } 532 return 0; 533} 534 535int do_stop(int nargs, char **args) 536{ 537 struct service *svc; 538 svc = service_find_by_name(args[1]); 539 if (svc) { 540 service_stop(svc); 541 } 542 return 0; 543} 544 545int do_restart(int nargs, char **args) 546{ 547 struct service *svc; 548 svc = service_find_by_name(args[1]); 549 if (svc) { 550 service_restart(svc); 551 } 552 return 0; 553} 554 555int do_powerctl(int nargs, char **args) 556{ 557 char command[PROP_VALUE_MAX]; 558 int res; 559 int len = 0; 560 int cmd = 0; 561 const char *reboot_target; 562 563 res = expand_props(command, args[1], sizeof(command)); 564 if (res) { 565 ERROR("powerctl: cannot expand '%s'\n", args[1]); 566 return -EINVAL; 567 } 568 569 if (strncmp(command, "shutdown", 8) == 0) { 570 cmd = ANDROID_RB_POWEROFF; 571 len = 8; 572 } else if (strncmp(command, "reboot", 6) == 0) { 573 cmd = ANDROID_RB_RESTART2; 574 len = 6; 575 } else { 576 ERROR("powerctl: unrecognized command '%s'\n", command); 577 return -EINVAL; 578 } 579 580 if (command[len] == ',') { 581 reboot_target = &command[len + 1]; 582 } else if (command[len] == '\0') { 583 reboot_target = ""; 584 } else { 585 ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]); 586 return -EINVAL; 587 } 588 589 return android_reboot(cmd, 0, reboot_target); 590} 591 592int do_trigger(int nargs, char **args) 593{ 594 action_for_each_trigger(args[1], action_add_queue_tail); 595 return 0; 596} 597 598int do_symlink(int nargs, char **args) 599{ 600 return symlink(args[1], args[2]); 601} 602 603int do_rm(int nargs, char **args) 604{ 605 return unlink(args[1]); 606} 607 608int do_rmdir(int nargs, char **args) 609{ 610 return rmdir(args[1]); 611} 612 613int do_sysclktz(int nargs, char **args) 614{ 615 struct timezone tz; 616 617 if (nargs != 2) 618 return -1; 619 620 memset(&tz, 0, sizeof(tz)); 621 tz.tz_minuteswest = atoi(args[1]); 622 if (settimeofday(NULL, &tz)) 623 return -1; 624 return 0; 625} 626 627int do_verity_load_state(int nargs, char **args) { 628 int mode = -1; 629 int rc = fs_mgr_load_verity_state(&mode); 630 if (rc == 0 && mode == VERITY_MODE_LOGGING) { 631 action_for_each_trigger("verity-logging", action_add_queue_tail); 632 } 633 return rc; 634} 635 636static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) { 637 property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(), 638 android::base::StringPrintf("%d", mode).c_str()); 639} 640 641int do_verity_update_state(int nargs, char** args) { 642 return fs_mgr_update_verity_state(verity_update_property); 643} 644 645int do_write(int nargs, char **args) 646{ 647 const char *path = args[1]; 648 const char *value = args[2]; 649 650 char expanded_value[256]; 651 if (expand_props(expanded_value, value, sizeof(expanded_value))) { 652 ERROR("cannot expand '%s' while writing to '%s'\n", value, path); 653 return -EINVAL; 654 } 655 return write_file(path, expanded_value); 656} 657 658int do_copy(int nargs, char **args) 659{ 660 char *buffer = NULL; 661 int rc = 0; 662 int fd1 = -1, fd2 = -1; 663 struct stat info; 664 int brtw, brtr; 665 char *p; 666 667 if (nargs != 3) 668 return -1; 669 670 if (stat(args[1], &info) < 0) 671 return -1; 672 673 if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0) 674 goto out_err; 675 676 if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0) 677 goto out_err; 678 679 if (!(buffer = (char*) malloc(info.st_size))) 680 goto out_err; 681 682 p = buffer; 683 brtr = info.st_size; 684 while(brtr) { 685 rc = read(fd1, p, brtr); 686 if (rc < 0) 687 goto out_err; 688 if (rc == 0) 689 break; 690 p += rc; 691 brtr -= rc; 692 } 693 694 p = buffer; 695 brtw = info.st_size; 696 while(brtw) { 697 rc = write(fd2, p, brtw); 698 if (rc < 0) 699 goto out_err; 700 if (rc == 0) 701 break; 702 p += rc; 703 brtw -= rc; 704 } 705 706 rc = 0; 707 goto out; 708out_err: 709 rc = -1; 710out: 711 if (buffer) 712 free(buffer); 713 if (fd1 >= 0) 714 close(fd1); 715 if (fd2 >= 0) 716 close(fd2); 717 return rc; 718} 719 720int do_chown(int nargs, char **args) { 721 /* GID is optional. */ 722 if (nargs == 3) { 723 if (lchown(args[2], decode_uid(args[1]), -1) == -1) 724 return -errno; 725 } else if (nargs == 4) { 726 if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1) 727 return -errno; 728 } else { 729 return -1; 730 } 731 return 0; 732} 733 734static mode_t get_mode(const char *s) { 735 mode_t mode = 0; 736 while (*s) { 737 if (*s >= '0' && *s <= '7') { 738 mode = (mode<<3) | (*s-'0'); 739 } else { 740 return -1; 741 } 742 s++; 743 } 744 return mode; 745} 746 747int do_chmod(int nargs, char **args) { 748 mode_t mode = get_mode(args[1]); 749 if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) { 750 return -errno; 751 } 752 return 0; 753} 754 755int do_restorecon(int nargs, char **args) { 756 int i; 757 int ret = 0; 758 759 for (i = 1; i < nargs; i++) { 760 if (restorecon(args[i]) < 0) 761 ret = -errno; 762 } 763 return ret; 764} 765 766int do_restorecon_recursive(int nargs, char **args) { 767 int i; 768 int ret = 0; 769 770 for (i = 1; i < nargs; i++) { 771 if (restorecon_recursive(args[i]) < 0) 772 ret = -errno; 773 } 774 return ret; 775} 776 777int do_loglevel(int nargs, char **args) { 778 int log_level; 779 char log_level_str[PROP_VALUE_MAX] = ""; 780 if (nargs != 2) { 781 ERROR("loglevel: missing argument\n"); 782 return -EINVAL; 783 } 784 785 if (expand_props(log_level_str, args[1], sizeof(log_level_str))) { 786 ERROR("loglevel: cannot expand '%s'\n", args[1]); 787 return -EINVAL; 788 } 789 log_level = atoi(log_level_str); 790 if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) { 791 ERROR("loglevel: invalid log level'%d'\n", log_level); 792 return -EINVAL; 793 } 794 klog_set_level(log_level); 795 return 0; 796} 797 798int do_load_persist_props(int nargs, char **args) { 799 if (nargs == 1) { 800 load_persist_props(); 801 return 0; 802 } 803 return -1; 804} 805 806int do_load_all_props(int nargs, char **args) { 807 if (nargs == 1) { 808 load_all_props(); 809 return 0; 810 } 811 return -1; 812} 813 814int do_wait(int nargs, char **args) 815{ 816 if (nargs == 2) { 817 return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT); 818 } else if (nargs == 3) { 819 return wait_for_file(args[1], atoi(args[2])); 820 } else 821 return -1; 822} 823 824/* 825 * Callback to make a directory from the ext4 code 826 */ 827static int do_installkeys_ensure_dir_exists(const char* dir) 828{ 829 if (make_dir(dir, 0700) && errno != EEXIST) { 830 return -1; 831 } 832 833 return 0; 834} 835 836int do_installkey(int nargs, char **args) 837{ 838 if (nargs != 2) { 839 return -1; 840 } 841 842 char prop_value[PROP_VALUE_MAX] = {0}; 843 property_get("ro.crypto.type", prop_value); 844 if (strcmp(prop_value, "file")) { 845 return 0; 846 } 847 848 return e4crypt_create_device_key(args[1], 849 do_installkeys_ensure_dir_exists); 850} 851