blob: d535a251422c343ac15e611361513bbfdde7abf6 [file] [log] [blame]
nkk7171c6c502017-01-05 23:55:05 +02001/*
2 Copyright 2017 TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25#include <sys/time.h>
26
27#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
28#include <signal.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31#endif
32
33#include <string>
34#include <vector>
35#include <sstream>
36
37#include "../../twcommon.h"
38#include "../../partitions.hpp"
39#include "../../twrp-functions.hpp"
40#include "../../gui/gui.hpp"
41
42using namespace std;
43
44extern "C" {
45 #include <cutils/properties.h>
46}
47
48/* Timeouts as defined by ServiceManager */
49
50/* The maximum amount of time to wait for a service to start or stop,
51 * in micro-seconds (really an approximation) */
52#define SLEEP_MAX_USEC 2000000 /* 2 seconds */
53/* The minimal sleeping interval between checking for the service's state
54 * when looping for SLEEP_MAX_USEC */
55#define SLEEP_MIN_USEC 200000 /* 200 msec */
56
57
58#define LOGDECRYPT(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
59#define LOGDECRYPT_KMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
60
61#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
62typedef struct {
63 string service_name;
64 string twrp_svc_name;
65 bool is_running;
66 bool resume;
67} AdditionalService;
68#endif
69
70FILE *fp_kmsg = NULL;
71bool has_timeout = false;
72
73#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
74bool has_strace = false;
75
76pid_t strace_init(void) {
77 if (!has_strace)
78 return -1;
79
80 pid_t pid;
81 switch(pid = fork())
82 {
83 case -1:
84 LOGDECRYPT_KMSG("forking strace_init failed: %d!\n", errno);
85 return -1;
86 case 0: // child
87 execl("/sbin/strace", "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL);
88 LOGDECRYPT_KMSG("strace_init fork failed: %d!\n", errno);
89 exit(-1);
90 default:
91 LOGDECRYPT_KMSG("Starting strace_init (pid=%d)\n", pid);
92 return pid;
93 }
94}
95#endif
96
97/* Convert a binary key of specified length into an ascii hex string equivalent,
98 * without the leading 0x and with null termination
99 *
100 * Original code from cryptfs.c
101 */
102string convert_key_to_hex_ascii(string master_key) {
103 size_t i;
104 unsigned char nibble;
105 string master_key_ascii = "";
106
107 for (i = 0; i < master_key.size(); ++i) {
108 nibble = (master_key[i] >> 4) & 0xf;
109 nibble += nibble > 9 ? 0x57 : 0x30;
110 master_key_ascii += nibble;
111
112 nibble = master_key[i] & 0xf;
113 nibble += nibble > 9 ? 0x57 : 0x30;
114 master_key_ascii += nibble;
115 }
116
117 return master_key_ascii;
118}
119
120string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, string expected_value = "not_empty") {
121 char prop_value[PROPERTY_VALUE_MAX];
122
123 if (expected_value == "not_empty") {
124 while (utimeout > 0) {
125 property_get(property_name.c_str(), prop_value, "error");
126 if (strcmp(prop_value, "error") != 0)
127 break;
128 LOGDECRYPT_KMSG("waiting for %s to get set\n", property_name.c_str());
129 utimeout -= SLEEP_MIN_USEC;
130 usleep(SLEEP_MIN_USEC);;
131 }
132 }
133 else {
134 while (utimeout > 0) {
135 property_get(property_name.c_str(), prop_value, "error");
136 if (strcmp(prop_value, expected_value.c_str()) == 0)
137 break;
138 LOGDECRYPT_KMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str());
139 utimeout -= SLEEP_MIN_USEC;
140 usleep(SLEEP_MIN_USEC);;
141 }
142 }
143 property_get(property_name.c_str(), prop_value, "error");
144
145 return prop_value;
146}
147
148bool Service_Exists(string initrc_svc) {
149 char prop_value[PROPERTY_VALUE_MAX];
150 string init_svc = "init.svc." + initrc_svc;
151 property_get(init_svc.c_str(), prop_value, "error");
152 return (strcmp(prop_value, "error") != 0);
153}
154
155bool Is_Service_Running(string initrc_svc) {
156 char prop_value[PROPERTY_VALUE_MAX];
157 string init_svc = "init.svc." + initrc_svc;
158 property_get(init_svc.c_str(), prop_value, "error");
159 return (strcmp(prop_value, "running") == 0);
160}
161
162bool Is_Service_Stopped(string initrc_svc) {
163 char prop_value[PROPERTY_VALUE_MAX];
164 string init_svc = "init.svc." + initrc_svc;
165 property_get(init_svc.c_str(), prop_value, "error");
166 return (strcmp(prop_value, "stopped") == 0);
167}
168
169bool Start_Service(string initrc_svc, int utimeout = SLEEP_MAX_USEC) {
170 string res = "error";
171 string init_svc = "init.svc." + initrc_svc;
172
173 property_set("ctl.start", initrc_svc.c_str());
174
175 res = wait_for_property(init_svc, utimeout, "running");
176
177 LOGDECRYPT("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str());
178
179 return (res == "running");
180}
181
182bool Stop_Service(string initrc_svc, int utimeout = SLEEP_MAX_USEC) {
183 string res = "error";
184
185 if (Service_Exists(initrc_svc)) {
186 string init_svc = "init.svc." + initrc_svc;
187 property_set("ctl.stop", initrc_svc.c_str());
188 res = wait_for_property(init_svc, utimeout, "stopped");
189 LOGDECRYPT("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str());
190 }
191
192 return (res == "stopped");
193}
194
195void output_dmesg_to_recoverylog(void) {
196 TWFunc::Exec_Cmd(
197 "echo \"---- DMESG LOG FOLLOWS ----\";"
198 "dmesg | grep 'DECRYPT\\|vold\\|qseecom\\|QSEECOM\\|keymaste\\|keystore\\|cmnlib';"
199 "echo \"---- DMESG LOG ENDS ----\""
200 );
201}
202
203void set_needed_props(void) {
204 // vold won't start without ro.storage_structure on Kitkat
205 string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
206 int sdkver = 20;
207 if (!sdkverstr.empty()) {
208 sdkver = atoi(sdkverstr.c_str());
209 }
210 if (sdkver <= 19) {
211 string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure");
212 if (!ro_storage_structure.empty())
213 property_set("ro.storage_structure", ro_storage_structure.c_str());
214 }
215}
216
217string vdc_cryptfs_cmd(string log_name) {
nkk71201d4b22017-03-06 22:06:31 +0200218 string cmd = "LD_LIBRARY_PATH=/system/lib64:/system/lib /system/bin/vdc cryptfs";
nkk7171c6c502017-01-05 23:55:05 +0200219
nkk71201d4b22017-03-06 22:06:31 +0200220#ifndef TW_CRYPTO_SYSTEM_VOLD_DEBUG
221 (void)log_name; // do nothing, but get rid of compiler warning in non debug builds
222#else
nkk7171c6c502017-01-05 23:55:05 +0200223 if (has_timeout && has_strace)
224 cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " /sbin/timeout -t 30 -s KILL env " + cmd;
225 else if (has_strace)
226 cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " -E " + cmd;
227 else
228#endif
229 if (has_timeout)
230 cmd = "/sbin/timeout -t 30 -s KILL env " + cmd;
231
232 return cmd;
233}
234
235int run_vdc(string Password) {
236 int res = -1;
237 struct timeval t1, t2;
238 string vdc_res;
239 int vdc_r1, vdc_r2, vdc_r3;
240
241 LOGDECRYPT("About to run vdc...\n");
242
243 // Wait for vold connection
244 gettimeofday(&t1, NULL);
245 t2 = t1;
246 while ((t2.tv_sec - t1.tv_sec) < 5) {
247 vdc_res.clear();
248 // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default"
249 res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("connect") + " getpwtype", vdc_res);
250 std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s)
251 vdc_r1 = vdc_r2 = vdc_r3 = -1;
252 sscanf(vdc_res.c_str(), "%d", &vdc_r1);
253 if (vdc_r1 == 213) {
254 char str_res[sizeof(int) + 1];
255 snprintf(str_res, sizeof(str_res), "%d", res);
256 vdc_res += "ret=";
257 vdc_res += str_res;
258 res = 0;
259 break;
260 }
261 LOGDECRYPT("Retrying connection to vold\n");
262 usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
263 gettimeofday(&t2, NULL);
264 }
265
266 if (res != 0)
267 return res;
268
269 LOGDECRYPT("Connected to vold (%s)\n", vdc_res.c_str());
270
271 // Input password from GUI, or default password
272 vdc_res.clear();
273 res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("passwd") + " checkpw '" + Password + "'", vdc_res);
274 std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s)
275 LOGDECRYPT("vdc cryptfs result (passwd): %s (ret=%d)\n", vdc_res.c_str(), res);
276 vdc_r1 = vdc_r2 = vdc_r3 = -1;
277 sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3);
278
279 if (vdc_r3 != 0) {
280 // try falling back to Lollipop hex passwords
281 string hexPassword = convert_key_to_hex_ascii(Password);
282 vdc_res.clear();
283 res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("hex_pw") + " checkpw '" + hexPassword + "'", vdc_res);
284 std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s)
285 LOGDECRYPT("vdc cryptfs result (hex_pw): %s (ret=%d)\n", vdc_res.c_str(), res);
286 vdc_r1 = vdc_r2 = vdc_r3 = -1;
287 sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3);
288 }
289
290 // vdc's return value is dependant upon source origin, it will either
291 // return 0 or vdc_r1, so disregard and focus on decryption instead
292 if (vdc_r3 == 0) {
293 // Decryption successful wait for crypto blk dev
294 wait_for_property("ro.crypto.fs_crypto_blkdev");
295 res = 0;
296 } else {
297 res = -1;
298 }
299
300 return res;
301}
302
303bool Symlink_Vendor_Folder(void) {
304 bool is_vendor_symlinked = false;
305
306 if (PartitionManager.Is_Mounted_By_Path("/vendor")) {
307 LOGDECRYPT("vendor partition mounted, skipping /vendor substitution\n");
308 }
309 else if (TWFunc::Path_Exists("/system/vendor")) {
310 LOGDECRYPT("Symlinking vendor folder...\n");
311 if (TWFunc::Path_Exists("/vendor") && rename("/vendor", "/vendor-orig") != 0) {
312 LOGDECRYPT("Failed to rename original /vendor folder: %s\n", strerror(errno));
313 } else {
314 TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster");
315 LOGDECRYPT_KMSG("Symlinking /system/vendor/lib64 to /vendor/lib64 (res=%d)\n",
316 symlink("/system/vendor/lib64", "/vendor/lib64")
317 );
318 LOGDECRYPT_KMSG("Symlinking /system/vendor/lib to /vendor/lib (res=%d)\n",
319 symlink("/system/vendor/lib", "/vendor/lib")
320 );
321 is_vendor_symlinked = true;
322 property_set("vold_decrypt.symlinked_vendor", "1");
323 }
324 }
325 return is_vendor_symlinked;
326}
327
328void Restore_Vendor_Folder(void) {
329 property_set("vold_decrypt.symlinked_vendor", "0");
330 TWFunc::removeDir("/vendor", false);
331 rename("/vendor-orig", "/vendor");
332}
333
334bool Symlink_Firmware_Folder(void) {
335 bool is_firmware_symlinked = false;
336
337 if (PartitionManager.Is_Mounted_By_Path("/firmware")) {
338 LOGDECRYPT("firmware partition mounted, skipping /firmware substitution\n");
339 } else {
340 LOGDECRYPT("Symlinking firmware folder...\n");
341 if (TWFunc::Path_Exists("/firmware") && rename("/firmware", "/firmware-orig") != 0) {
342 LOGDECRYPT("Failed to rename original /firmware folder: %s\n", strerror(errno));
343 } else {
344 TWFunc::Recursive_Mkdir("/firmware/image");
345 is_firmware_symlinked = true;
346 property_set("vold_decrypt.symlinked_firmware", "1");
347 }
348 }
349 return is_firmware_symlinked;
350}
351
352void Restore_Firmware_Folder(void) {
353 property_set("vold_decrypt.symlinked_firmware", "0");
354 TWFunc::removeDir("/firmware", false);
355 rename("/firmware-orig", "/firmware");
356}
357
358void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) {
359 if (!is_vendor_symlinked && !is_firmware_symlinked)
360 return;
361
362 LOGDECRYPT("Symlinking firmware files...\n");
363 string result_of_find;
364 TWFunc::Exec_Cmd("find /system -name keymaste*.* -type f -o -name cmnlib.* -type f 2>/dev/null", result_of_find);
365
366 stringstream ss(result_of_find);
367 string line;
368 int count = 0;
369
370 while(getline(ss, line)) {
371 const char *fwfile = line.c_str();
372 string base_name = TWFunc::Get_Filename(line);
373 count++;
374
375 if (is_firmware_symlinked) {
376 LOGDECRYPT_KMSG("Symlinking %s to /firmware/image/ (res=%d)\n", fwfile,
377 symlink(fwfile, ("/firmware/image/" + base_name).c_str())
378 );
379 }
380
381 if (is_vendor_symlinked) {
382 LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/ (res=%d)\n", fwfile,
383 symlink(fwfile, ("/vendor/firmware/" + base_name).c_str())
384 );
385
386 LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/keymaster/ (res=%d)\n", fwfile,
387 symlink(fwfile, ("/vendor/firmware/keymaster/" + base_name).c_str())
388 );
389 }
390 }
391 LOGDECRYPT("%d file(s) symlinked.\n", count);
392}
393
394#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
395vector<AdditionalService> Get_List_Of_Additional_Services (void) {
396 vector<AdditionalService> services;
397
398 vector<string> service_names = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_SERVICES, " ");
399
400 for (size_t i = 0; i < service_names.size(); ++i) {
401 AdditionalService svc;
402 svc.service_name = service_names[i];
403 services.push_back(svc);
404 }
405
406 return services;
407}
408#endif
409
410int vold_decrypt(string Password)
411{
412 int res;
413 bool output_dmesg_to_log = false;
414 bool is_vendor_symlinked = false;
415 bool is_firmware_symlinked = false;
416 bool is_vold_running = false;
417
418 if (Password.empty()) {
419 LOGDECRYPT("vold_decrypt: password is empty!\n");
420 return -1;
421 }
422
423 // Mount system and check for vold and vdc
424 if (!PartitionManager.Mount_By_Path("/system", true)) {
425 return -1;
426 } else if (!TWFunc::Path_Exists("/system/bin/vold")) {
427 LOGDECRYPT("ERROR: /system/bin/vold not found, aborting.\n");
428 gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vold"));
429 return -1;
430 } else if (!TWFunc::Path_Exists("/system/bin/vdc")) {
431 LOGDECRYPT("ERROR: /system/bin/vdc not found, aborting.\n");
432 gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vdc"));
433 return -1;
434 }
435
436 fp_kmsg = fopen("/dev/kmsg", "a");
437
438 LOGDECRYPT("TW_CRYPTO_USE_SYSTEM_VOLD := true\n");
439 LOGDECRYPT("Attempting to use system's vold for decryption...\n");
440
441#ifndef TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT
442 has_timeout = TWFunc::Path_Exists("/sbin/timeout");
443 if (!has_timeout)
444 LOGDECRYPT("timeout binary not found, disabling timeout in vold_decrypt!\n");
445#endif
446
447#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
448 has_strace = TWFunc::Path_Exists("/sbin/strace");
449 if (!has_strace)
450 LOGDECRYPT("strace binary not found, disabling strace in vold_decrypt!\n");
451 pid_t pid_strace = strace_init();
452#endif
453
454#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
455 vector<AdditionalService> Services = Get_List_Of_Additional_Services();
456
457 // Check if TWRP is running any of the services
458 for (size_t i = 0; i < Services.size(); ++i) {
459 if (Service_Exists(Services[i].service_name))
460 Services[i].twrp_svc_name = Services[i].service_name;
461 else if (Service_Exists("sbin" + Services[i].service_name))
462 Services[i].twrp_svc_name = "sbin" + Services[i].service_name;
463 else
464 Services[i].twrp_svc_name.clear();
465
466 if (!Services[i].twrp_svc_name.empty() && !Is_Service_Stopped(Services[i].twrp_svc_name)) {
467 Services[i].resume = true;
468 Stop_Service(Services[i].twrp_svc_name);
469 } else
470 Services[i].resume = false;
471
472 // vold_decrypt system services have to be named sys_{service} in the .rc files
473 Services[i].service_name = "sys_" + Services[i].service_name;
474 }
475#endif
476
477 LOGDECRYPT("Setting up folders and permissions...\n");
478 is_vendor_symlinked = Symlink_Vendor_Folder();
479 is_firmware_symlinked = Symlink_Firmware_Folder();
480 Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked);
481
482 set_needed_props();
483
484 // Start services needed for vold decrypt
485 LOGDECRYPT("Starting services...\n");
486#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
487 for (size_t i = 0; i < Services.size(); ++i) {
488 Services[i].is_running = Start_Service(Services[i].service_name);
489 }
490#endif
491 is_vold_running = Start_Service("sys_vold");
492
493 if (is_vold_running) {
494
495#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
496 for (size_t i = 0; i < Services.size(); ++i) {
497 if (!Is_Service_Running(Services[i].service_name) && Services[i].resume) {
498 // if system_service has died restart the twrp_service
499 LOGDECRYPT("%s is not running, resuming %s!\n", Services[i].service_name.c_str(), Services[i].twrp_svc_name.c_str());
500 Start_Service(Services[i].twrp_svc_name);
501 }
502 }
503#endif
504
505 res = run_vdc(Password);
506
507 if (res != 0) {
508 // Decryption was unsuccessful
509 LOGDECRYPT("Decryption failed\n");
510 output_dmesg_to_log = true;
511 }
512 } else {
513 LOGDECRYPT("Failed to start vold\n");
514 TWFunc::Exec_Cmd("echo \"$(getprop | grep init.svc)\" >> /dev/kmsg");
515 output_dmesg_to_log = true;
516 }
517
518 // Stop services needed for vold decrypt so /system can be unmounted
519 LOGDECRYPT("Stopping services...\n");
520 Stop_Service("sys_vold");
521#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
522 for (size_t i = 0; i < Services.size(); ++i) {
523 if (!Is_Service_Running(Services[i].service_name) && Services[i].resume)
524 Stop_Service(Services[i].twrp_svc_name);
525 else
526 Stop_Service(Services[i].service_name);
527 }
528#endif
529
530 if (is_firmware_symlinked)
531 Restore_Firmware_Folder();
532 if (is_vendor_symlinked)
533 Restore_Vendor_Folder();
534
535 if (!PartitionManager.UnMount_By_Path("/system", true)) {
536 // PartitionManager failed to unmount /system, this should not happen,
537 // but in case it does, do a lazy unmount
538 LOGDECRYPT("WARNING: system could not be unmounted normally!\n");
539 TWFunc::Exec_Cmd("umount -l /system");
540 }
541
542 LOGDECRYPT("Finished.\n");
543
544#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
545 // Restart previously running services
546 for (size_t i = 0; i < Services.size(); ++i) {
547 if (Services[i].resume)
548 Start_Service(Services[i].twrp_svc_name);
549 }
550#endif
551
552#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
553 if (pid_strace > 0) {
554 LOGDECRYPT_KMSG("Stopping strace_init (pid=%d)\n", pid_strace);
nkk7171c6c502017-01-05 23:55:05 +0200555 int timeout;
556 int status;
557 pid_t retpid = waitpid(pid_strace, &status, WNOHANG);
558
559 kill(pid_strace, SIGTERM);
560 for (timeout = 5; retpid == 0 && timeout; --timeout) {
561 sleep(1);
562 retpid = waitpid(pid_strace, &status, WNOHANG);
563 }
564 if (retpid)
565 LOGDECRYPT_KMSG("strace_init terminated successfully\n");
566 else {
567 // SIGTERM didn't work, kill it instead
568 kill(pid_strace, SIGKILL);
569 for (timeout = 5; retpid == 0 && timeout; --timeout) {
570 sleep(1);
571 retpid = waitpid(pid_strace, &status, WNOHANG);
572 }
573 if (retpid)
574 LOGDECRYPT_KMSG("strace_init killed successfully\n");
575 else
576 LOGDECRYPT_KMSG("strace_init took too long to kill, may be a zombie process\n");
577 }
578 }
579 output_dmesg_to_log = true;
580#endif
581
582 // Finish up and exit
583 if (fp_kmsg) {
584 fflush(fp_kmsg);
585 fclose(fp_kmsg);
586 }
587
588 if (output_dmesg_to_log)
589 output_dmesg_to_recoverylog();
590
591 // Finally check if crypto device is up
592 if (wait_for_property("ro.crypto.fs_crypto_blkdev", 0) != "error")
593 res = 0;
594 else
595 res = -1;
596
597 return res;
598}