blob: 1bb58cc67146a4725312800d2877f6aadf0a055b [file] [log] [blame]
/*
* Copyright (C) 2019 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <lib/libtar.h>
#include <zlib.h>
#include <fs_mgr.h>
#include "roots.h"
#include "bu.h"
using namespace android;
static int verify_sod() {
const char* key;
char value[PROPERTY_VALUE_MAX];
char sodbuf[PROP_LINE_LEN * 10];
size_t len;
len = sizeof(sodbuf);
if (tar_extract_file_contents(tar, sodbuf, &len) != 0) {
logmsg("tar_verify_sod: failed to extract file\n");
return -1;
}
char val_hashname[PROPERTY_VALUE_MAX];
memset(val_hashname, 0, sizeof(val_hashname));
char val_product[PROPERTY_VALUE_MAX];
memset(val_product, 0, sizeof(val_product));
char* p = sodbuf;
char* q;
while ((q = strchr(p, '\n')) != NULL) {
char* key = p;
*q = '\0';
logmsg("verify_sod: line=%s\n", p);
p = q + 1;
char* val = strchr(key, '=');
if (val) {
*val = '\0';
++val;
if (strcmp(key, "hash.name") == 0) {
strncpy(val_hashname, val, sizeof(val_hashname));
}
if (strcmp(key, "ro.product.device") == 0) {
strncpy(val_product, val, sizeof(val_product));
}
if (strncmp(key, "fs.", 3) == 0) {
char* name = key + 3;
char* attr = strchr(name, '.');
if (attr) {
*attr = '\0';
++attr;
part_add(name);
struct partspec* part = part_find(name);
if (!strcmp(attr, "size")) {
part->size = strtoul(val, NULL, 0);
}
if (!strcmp(attr, "used")) {
part->used = strtoul(val, NULL, 0);
}
}
}
}
}
if (!val_hashname[0]) {
logmsg("verify_sod: did not find hash.name\n");
return -1;
}
hash_name = strdup(val_hashname);
if (!val_product[0]) {
logmsg("verify_sod: did not find ro.product.device\n");
return -1;
}
key = "ro.product.device";
property_get(key, value, "");
if (strcmp(val_product, value) != 0) {
logmsg("verify_sod: product does not match\n");
return -1;
}
return 0;
}
static int verify_eod(size_t actual_hash_datalen, SHA_CTX* actual_sha_ctx,
MD5_CTX* actual_md5_ctx) {
int rc = -1;
char eodbuf[PROP_LINE_LEN * 10];
size_t len;
len = sizeof(eodbuf);
if (tar_extract_file_contents(tar, eodbuf, &len) != 0) {
logmsg("verify_eod: failed to extract file\n");
return -1;
}
size_t reported_datalen = 0;
char reported_hash[HASH_MAX_STRING_LENGTH];
memset(reported_hash, 0, sizeof(reported_hash));
char* p = eodbuf;
char* q;
while ((q = strchr(p, '\n')) != NULL) {
char* key = p;
*q = '\0';
logmsg("verify_eod: line=%s\n", p);
p = q + 1;
char* val = strchr(key, '=');
if (val) {
*val = '\0';
++val;
if (strcmp(key, "hash.datalen") == 0) {
reported_datalen = strtoul(val, NULL, 0);
}
if (strcmp(key, "hash.value") == 0) {
memset(reported_hash, 0, sizeof(reported_hash));
strncpy(reported_hash, val, sizeof(reported_hash));
}
}
}
unsigned char digest[HASH_MAX_LENGTH];
char hexdigest[HASH_MAX_STRING_LENGTH];
int n;
if (hash_name != NULL && !strcasecmp(hash_name, "sha1")) {
SHA1_Final(digest, actual_sha_ctx);
for (n = 0; n < SHA_DIGEST_LENGTH; ++n) {
sprintf(hexdigest + 2 * n, "%02x", digest[n]);
}
} else { // default to md5
MD5_Final(digest, actual_md5_ctx);
for (n = 0; n < MD5_DIGEST_LENGTH; ++n) {
sprintf(hexdigest + 2 * n, "%02x", digest[n]);
}
}
logmsg("verify_eod: expected=%d,%s\n", actual_hash_datalen, hexdigest);
logmsg("verify_eod: reported=%d,%s\n", reported_datalen, reported_hash);
if ((reported_datalen == actual_hash_datalen) &&
(memcmp(hexdigest, reported_hash, strlen(hexdigest)) == 0)) {
rc = 0;
}
return rc;
}
int do_restore(int argc, char** argv) {
int rc = 0;
ssize_t len;
const char* compress = "none";
char buf[512];
len = recv(adb_ifd, buf, sizeof(buf), MSG_PEEK);
if (len < 0) {
logmsg("do_restore: peek failed (%d:%s)\n", rc, strerror(errno));
return -1;
}
if (len < 2) {
logmsg("do_restore: peek returned %d\n", len);
return -1;
}
if (buf[0] == 0x1f && buf[1] == 0x8b) {
logmsg("do_restore: is gzip\n");
compress = "gzip";
}
create_tar(adb_ifd, compress, "r");
size_t save_hash_datalen;
SHA_CTX save_sha_ctx;
MD5_CTX save_md5_ctx;
while (1) {
save_hash_datalen = hash_datalen;
memcpy(&save_sha_ctx, &sha_ctx, sizeof(SHA_CTX));
memcpy(&save_md5_ctx, &md5_ctx, sizeof(MD5_CTX));
rc = th_read(tar);
if (rc != 0) {
if (rc == 1) { // EOF
rc = 0;
}
break;
}
char* pathname = th_get_pathname(tar);
logmsg("do_restore: extract %s\n", pathname);
if (!strcmp(pathname, "SOD")) {
rc = verify_sod();
logmsg("do_restore: tar_verify_sod returned %d\n", rc);
} else if (!strcmp(pathname, "EOD")) {
rc = verify_eod(save_hash_datalen, &save_sha_ctx, &save_md5_ctx);
logmsg("do_restore: tar_verify_eod returned %d\n", rc);
} else {
char mnt[PATH_MAX];
snprintf(mnt, sizeof(mnt), "/%s", pathname);
Volume* vol = volume_for_mount_point(mnt);
if (vol != NULL && vol->fs_type != NULL) {
partspec* curpart = part_find(pathname);
part_set(curpart);
rc = tar_extract_file(tar, vol->blk_device);
} else {
logmsg("do_restore: cannot find volume for %s\n", mnt);
}
}
free(pathname);
if (rc != 0) {
logmsg("do_restore: extract failed, rc=%d\n", rc);
break;
}
}
tar_close(tar);
logmsg("do_restore: rc=%d\n", rc);
free(hash_name);
hash_name = NULL;
return rc;
}