upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
39
android/system/extras/libfec/Android.bp
Normal file
39
android/system/extras/libfec/Android.bp
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2015 The Android Open Source Project
|
||||
|
||||
cc_library {
|
||||
name: "libfec",
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"fec_open.cpp",
|
||||
"fec_read.cpp",
|
||||
"fec_verity.cpp",
|
||||
"fec_process.cpp",
|
||||
],
|
||||
cflags: ["-Wall", "-Werror", "-O3", "-D_LARGEFILE64_SOURCE"],
|
||||
export_include_dirs: ["include"],
|
||||
// Exported header include/fec/io.h includes crypto_utils headers.
|
||||
export_static_lib_headers: ["libcrypto_utils"],
|
||||
|
||||
static_libs: [
|
||||
"libext4_utils",
|
||||
"libfec_rs",
|
||||
"libsquashfs_utils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
"libcutils",
|
||||
"libbase",
|
||||
],
|
||||
|
||||
target: {
|
||||
host: {
|
||||
cflags: ["-D_GNU_SOURCE", "-DFEC_NO_KLOG"]
|
||||
},
|
||||
linux: {
|
||||
sanitize: {
|
||||
misc_undefined: ["integer"],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
clang: true,
|
||||
}
|
5
android/system/extras/libfec/Android.mk
Normal file
5
android/system/extras/libfec/Android.mk
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright 2015 The Android Open Source Project
|
||||
#
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(LOCAL_PATH)/test/Android.mk
|
190
android/system/extras/libfec/NOTICE
Normal file
190
android/system/extras/libfec/NOTICE
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
Copyright (c) 2015, The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
570
android/system/extras/libfec/fec_open.cpp
Normal file
570
android/system/extras/libfec/fec_open.cpp
Normal file
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ext4_utils/ext4_sb.h>
|
||||
|
||||
extern "C" {
|
||||
#include <squashfs_utils.h>
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/fs.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/disk.h>
|
||||
#define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
|
||||
#define fdatasync(fd) fcntl((fd), F_FULLFSYNC)
|
||||
#endif
|
||||
|
||||
#include "fec_private.h"
|
||||
|
||||
/* used by `find_offset'; returns metadata size for a file size `size' and
|
||||
`roots' Reed-Solomon parity bytes */
|
||||
using size_func = uint64_t (*)(uint64_t size, int roots);
|
||||
|
||||
/* performs a binary search to find a metadata offset from a file so that
|
||||
the metadata size matches function `get_real_size(size, roots)', using
|
||||
the approximate size returned by `get_appr_size' as a starting point */
|
||||
static int find_offset(uint64_t file_size, int roots, uint64_t *offset,
|
||||
size_func get_appr_size, size_func get_real_size)
|
||||
{
|
||||
check(offset);
|
||||
check(get_appr_size);
|
||||
check(get_real_size);
|
||||
|
||||
if (file_size % FEC_BLOCKSIZE) {
|
||||
/* must be a multiple of block size */
|
||||
error("file size not multiple of " stringify(FEC_BLOCKSIZE));
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t mi = get_appr_size(file_size, roots);
|
||||
uint64_t lo = file_size - mi * 2;
|
||||
uint64_t hi = file_size - mi / 2;
|
||||
|
||||
while (lo < hi) {
|
||||
mi = ((hi + lo) / (2 * FEC_BLOCKSIZE)) * FEC_BLOCKSIZE;
|
||||
uint64_t total = mi + get_real_size(mi, roots);
|
||||
|
||||
if (total < file_size) {
|
||||
lo = mi + FEC_BLOCKSIZE;
|
||||
} else if (total > file_size) {
|
||||
hi = mi;
|
||||
} else {
|
||||
*offset = mi;
|
||||
debug("file_size = %" PRIu64 " -> offset = %" PRIu64, file_size,
|
||||
mi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
warn("could not determine offset");
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* returns verity metadata size for a `size' byte file */
|
||||
static uint64_t get_verity_size(uint64_t size, int)
|
||||
{
|
||||
return VERITY_METADATA_SIZE + verity_get_size(size, NULL, NULL);
|
||||
}
|
||||
|
||||
/* computes the verity metadata offset for a file with size `f->size' */
|
||||
static int find_verity_offset(fec_handle *f, uint64_t *offset)
|
||||
{
|
||||
check(f);
|
||||
check(offset);
|
||||
|
||||
return find_offset(f->data_size, 0, offset, get_verity_size,
|
||||
get_verity_size);
|
||||
}
|
||||
|
||||
/* attempts to read and validate an ecc header from file position `offset' */
|
||||
static int parse_ecc_header(fec_handle *f, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(f->ecc.rsn > 0 && f->ecc.rsn < FEC_RSM);
|
||||
check(f->size > sizeof(fec_header));
|
||||
|
||||
debug("offset = %" PRIu64, offset);
|
||||
|
||||
if (offset > f->size - sizeof(fec_header)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fec_header header;
|
||||
|
||||
/* there's obviously no ecc data at this point, so there is no need to
|
||||
call fec_pread to access this data */
|
||||
if (!raw_pread(f, &header, sizeof(fec_header), offset)) {
|
||||
error("failed to read: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* move offset back to the beginning of the block for validating header */
|
||||
offset -= offset % FEC_BLOCKSIZE;
|
||||
|
||||
if (header.magic != FEC_MAGIC) {
|
||||
return -1;
|
||||
}
|
||||
if (header.version != FEC_VERSION) {
|
||||
error("unsupported ecc version: %u", header.version);
|
||||
return -1;
|
||||
}
|
||||
if (header.size != sizeof(fec_header)) {
|
||||
error("unexpected ecc header size: %u", header.size);
|
||||
return -1;
|
||||
}
|
||||
if (header.roots == 0 || header.roots >= FEC_RSM) {
|
||||
error("invalid ecc roots: %u", header.roots);
|
||||
return -1;
|
||||
}
|
||||
if (f->ecc.roots != (int)header.roots) {
|
||||
error("unexpected number of roots: %d vs %u", f->ecc.roots,
|
||||
header.roots);
|
||||
return -1;
|
||||
}
|
||||
if (header.fec_size % header.roots ||
|
||||
header.fec_size % FEC_BLOCKSIZE) {
|
||||
error("inconsistent ecc size %u", header.fec_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->data_size = header.inp_size;
|
||||
f->ecc.blocks = fec_div_round_up(f->data_size, FEC_BLOCKSIZE);
|
||||
f->ecc.rounds = fec_div_round_up(f->ecc.blocks, f->ecc.rsn);
|
||||
|
||||
if (header.fec_size !=
|
||||
(uint32_t)f->ecc.rounds * f->ecc.roots * FEC_BLOCKSIZE) {
|
||||
error("inconsistent ecc size %u", header.fec_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->ecc.size = header.fec_size;
|
||||
f->ecc.start = header.inp_size;
|
||||
|
||||
/* validate encoding data; caller may opt not to use it if invalid */
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
|
||||
uint8_t buf[FEC_BLOCKSIZE];
|
||||
uint32_t n = 0;
|
||||
uint32_t len = FEC_BLOCKSIZE;
|
||||
|
||||
while (n < f->ecc.size) {
|
||||
if (len > f->ecc.size - n) {
|
||||
len = f->ecc.size - n;
|
||||
}
|
||||
|
||||
if (!raw_pread(f, buf, len, f->ecc.start + n)) {
|
||||
error("failed to read ecc: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
SHA256_Update(&ctx, buf, len);
|
||||
n += len;
|
||||
}
|
||||
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256_Final(hash, &ctx);
|
||||
|
||||
f->ecc.valid = !memcmp(hash, header.hash, SHA256_DIGEST_LENGTH);
|
||||
|
||||
if (!f->ecc.valid) {
|
||||
warn("ecc data not valid");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* attempts to read an ecc header from `offset', and checks for a backup copy
|
||||
at the end of the block if the primary header is not valid */
|
||||
static int parse_ecc(fec_handle *f, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(offset % FEC_BLOCKSIZE == 0);
|
||||
check(offset < UINT64_MAX - FEC_BLOCKSIZE);
|
||||
|
||||
/* check the primary header at the beginning of the block */
|
||||
if (parse_ecc_header(f, offset) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check the backup header at the end of the block */
|
||||
if (parse_ecc_header(f, offset + FEC_BLOCKSIZE - sizeof(fec_header)) == 0) {
|
||||
warn("using backup ecc header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reads the squashfs superblock and returns the size of the file system in
|
||||
`offset' */
|
||||
static int get_squashfs_size(fec_handle *f, uint64_t *offset)
|
||||
{
|
||||
check(f);
|
||||
check(offset);
|
||||
|
||||
size_t sb_size = squashfs_get_sb_size();
|
||||
check(sb_size <= SSIZE_MAX);
|
||||
|
||||
uint8_t buffer[sb_size];
|
||||
|
||||
if (fec_pread(f, buffer, sizeof(buffer), 0) != (ssize_t)sb_size) {
|
||||
error("failed to read superblock: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
squashfs_info sq;
|
||||
|
||||
if (squashfs_parse_sb_buffer(buffer, &sq) < 0) {
|
||||
error("failed to parse superblock: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*offset = sq.bytes_used_4K_padded;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reads the ext4 superblock and returns the size of the file system in
|
||||
`offset' */
|
||||
static int get_ext4_size(fec_handle *f, uint64_t *offset)
|
||||
{
|
||||
check(f);
|
||||
check(f->size > 1024 + sizeof(ext4_super_block));
|
||||
check(offset);
|
||||
|
||||
ext4_super_block sb;
|
||||
|
||||
if (fec_pread(f, &sb, sizeof(sb), 1024) != sizeof(sb)) {
|
||||
error("failed to read superblock: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs_info info;
|
||||
info.len = 0; /* only len is set to 0 to ask the device for real size. */
|
||||
|
||||
if (ext4_parse_sb(&sb, &info) != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*offset = info.len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* attempts to determine file system size, if no fs type is specified in
|
||||
`f->flags', tries all supported types, and returns the size in `offset' */
|
||||
static int get_fs_size(fec_handle *f, uint64_t *offset)
|
||||
{
|
||||
check(f);
|
||||
check(offset);
|
||||
|
||||
if (f->flags & FEC_FS_EXT4) {
|
||||
return get_ext4_size(f, offset);
|
||||
} else if (f->flags & FEC_FS_SQUASH) {
|
||||
return get_squashfs_size(f, offset);
|
||||
} else {
|
||||
/* try all alternatives */
|
||||
int rc = get_ext4_size(f, offset);
|
||||
|
||||
if (rc == 0) {
|
||||
debug("found ext4fs");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = get_squashfs_size(f, offset);
|
||||
|
||||
if (rc == 0) {
|
||||
debug("found squashfs");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* locates, validates, and loads verity metadata from `f->fd' */
|
||||
static int load_verity(fec_handle *f)
|
||||
{
|
||||
check(f);
|
||||
debug("size = %" PRIu64 ", flags = %d", f->data_size, f->flags);
|
||||
|
||||
uint64_t offset = f->data_size - VERITY_METADATA_SIZE;
|
||||
|
||||
/* verity header is at the end of the data area */
|
||||
if (verity_parse_header(f, offset) == 0) {
|
||||
debug("found at %" PRIu64 " (start %" PRIu64 ")", offset,
|
||||
f->verity.hash_start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("trying legacy formats");
|
||||
|
||||
/* legacy format at the end of the partition */
|
||||
if (find_verity_offset(f, &offset) == 0 &&
|
||||
verity_parse_header(f, offset) == 0) {
|
||||
debug("found at %" PRIu64 " (start %" PRIu64 ")", offset,
|
||||
f->verity.hash_start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* legacy format after the file system, but not at the end */
|
||||
int rc = get_fs_size(f, &offset);
|
||||
|
||||
if (rc == 0) {
|
||||
debug("file system size = %" PRIu64, offset);
|
||||
rc = verity_parse_header(f, offset);
|
||||
|
||||
if (rc == 0) {
|
||||
debug("found at %" PRIu64 " (start %" PRIu64 ")", offset,
|
||||
f->verity.hash_start);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* locates, validates, and loads ecc data from `f->fd' */
|
||||
static int load_ecc(fec_handle *f)
|
||||
{
|
||||
check(f);
|
||||
debug("size = %" PRIu64, f->data_size);
|
||||
|
||||
uint64_t offset = f->data_size - FEC_BLOCKSIZE;
|
||||
|
||||
if (parse_ecc(f, offset) == 0) {
|
||||
debug("found at %" PRIu64 " (start %" PRIu64 ")", offset,
|
||||
f->ecc.start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* sets `f->size' to the size of the file or block device */
|
||||
static int get_size(fec_handle *f)
|
||||
{
|
||||
check(f);
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (fstat(f->fd, &st) == -1) {
|
||||
error("fstat failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
debug("block device");
|
||||
|
||||
if (ioctl(f->fd, BLKGETSIZE64, &f->size) == -1) {
|
||||
error("ioctl failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
debug("file");
|
||||
f->size = st.st_size;
|
||||
} else {
|
||||
error("unsupported type %d", (int)st.st_mode);
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* clears fec_handle fiels to safe values */
|
||||
static void reset_handle(fec_handle *f)
|
||||
{
|
||||
f->fd = -1;
|
||||
f->flags = 0;
|
||||
f->mode = 0;
|
||||
f->errors = 0;
|
||||
f->data_size = 0;
|
||||
f->pos = 0;
|
||||
f->size = 0;
|
||||
|
||||
memset(&f->ecc, 0, sizeof(f->ecc));
|
||||
memset(&f->verity, 0, sizeof(f->verity));
|
||||
}
|
||||
|
||||
/* closes and flushes `f->fd' and releases any memory allocated for `f' */
|
||||
int fec_close(struct fec_handle *f)
|
||||
{
|
||||
check(f);
|
||||
|
||||
if (f->fd != -1) {
|
||||
if (f->mode & O_RDWR && fdatasync(f->fd) == -1) {
|
||||
warn("fdatasync failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
TEMP_FAILURE_RETRY(close(f->fd));
|
||||
}
|
||||
|
||||
if (f->verity.hash) {
|
||||
delete[] f->verity.hash;
|
||||
}
|
||||
if (f->verity.salt) {
|
||||
delete[] f->verity.salt;
|
||||
}
|
||||
if (f->verity.table) {
|
||||
delete[] f->verity.table;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&f->mutex);
|
||||
|
||||
reset_handle(f);
|
||||
delete f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* populates `data' from the internal data in `f', returns a value <0 if verity
|
||||
metadata is not available in `f->fd' */
|
||||
int fec_verity_get_metadata(struct fec_handle *f, struct fec_verity_metadata *data)
|
||||
{
|
||||
check(f);
|
||||
check(data);
|
||||
|
||||
if (!f->verity.metadata_start) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
check(f->data_size < f->size);
|
||||
check(f->data_size <= f->verity.hash_start);
|
||||
check(f->data_size <= f->verity.metadata_start);
|
||||
check(f->verity.table);
|
||||
|
||||
data->disabled = f->verity.disabled;
|
||||
data->data_size = f->data_size;
|
||||
memcpy(data->signature, f->verity.header.signature,
|
||||
sizeof(data->signature));
|
||||
memcpy(data->ecc_signature, f->verity.ecc_header.signature,
|
||||
sizeof(data->ecc_signature));
|
||||
data->table = f->verity.table;
|
||||
data->table_length = f->verity.header.length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* populates `data' from the internal data in `f', returns a value <0 if ecc
|
||||
metadata is not available in `f->fd' */
|
||||
int fec_ecc_get_metadata(struct fec_handle *f, struct fec_ecc_metadata *data)
|
||||
{
|
||||
check(f);
|
||||
check(data);
|
||||
|
||||
if (!f->ecc.start) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
check(f->data_size < f->size);
|
||||
check(f->ecc.start >= f->data_size);
|
||||
check(f->ecc.start < f->size);
|
||||
check(f->ecc.start % FEC_BLOCKSIZE == 0)
|
||||
|
||||
data->valid = f->ecc.valid;
|
||||
data->roots = f->ecc.roots;
|
||||
data->blocks = f->ecc.blocks;
|
||||
data->rounds = f->ecc.rounds;
|
||||
data->start = f->ecc.start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* populates `data' from the internal status in `f' */
|
||||
int fec_get_status(struct fec_handle *f, struct fec_status *s)
|
||||
{
|
||||
check(f);
|
||||
check(s);
|
||||
|
||||
s->flags = f->flags;
|
||||
s->mode = f->mode;
|
||||
s->errors = f->errors;
|
||||
s->data_size = f->data_size;
|
||||
s->size = f->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* opens `path' using given options and returns a fec_handle in `handle' if
|
||||
successful */
|
||||
int fec_open(struct fec_handle **handle, const char *path, int mode, int flags,
|
||||
int roots)
|
||||
{
|
||||
check(path);
|
||||
check(handle);
|
||||
check(roots > 0 && roots < FEC_RSM);
|
||||
|
||||
debug("path = %s, mode = %d, flags = %d, roots = %d", path, mode, flags,
|
||||
roots);
|
||||
|
||||
if (mode & (O_CREAT | O_TRUNC | O_EXCL | O_WRONLY)) {
|
||||
/* only reading and updating existing files is supported */
|
||||
error("failed to open '%s': (unsupported mode %d)", path, mode);
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fec::handle f(new (std::nothrow) fec_handle, fec_close);
|
||||
|
||||
if (unlikely(!f)) {
|
||||
error("failed to allocate file handle");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
reset_handle(f.get());
|
||||
|
||||
f->mode = mode;
|
||||
f->ecc.roots = roots;
|
||||
f->ecc.rsn = FEC_RSM - roots;
|
||||
f->flags = flags;
|
||||
|
||||
if (unlikely(pthread_mutex_init(&f->mutex, NULL) != 0)) {
|
||||
error("failed to create a mutex: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->fd = TEMP_FAILURE_RETRY(open(path, mode | O_CLOEXEC));
|
||||
|
||||
if (f->fd == -1) {
|
||||
error("failed to open '%s': %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_size(f.get()) == -1) {
|
||||
error("failed to get size for '%s': %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->data_size = f->size; /* until ecc and/or verity are loaded */
|
||||
|
||||
if (load_ecc(f.get()) == -1) {
|
||||
debug("error-correcting codes not found from '%s'", path);
|
||||
}
|
||||
|
||||
if (load_verity(f.get()) == -1) {
|
||||
debug("verity metadata not found from '%s'", path);
|
||||
}
|
||||
|
||||
*handle = f.release();
|
||||
return 0;
|
||||
}
|
174
android/system/extras/libfec/fec_private.h
Normal file
174
android/system/extras/libfec/fec_private.h
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#ifndef __FEC_PRIVATE_H__
|
||||
#define __FEC_PRIVATE_H__
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
#include <fec/ecc.h>
|
||||
#include <fec/io.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <utils/Compat.h>
|
||||
|
||||
/* processing parameters */
|
||||
#define WORK_MIN_THREADS 1
|
||||
#define WORK_MAX_THREADS 64
|
||||
|
||||
/* verity parameters */
|
||||
#define VERITY_CACHE_BLOCKS 4096
|
||||
#define VERITY_NO_CACHE UINT64_MAX
|
||||
|
||||
/* verity definitions */
|
||||
#define VERITY_METADATA_SIZE (8 * FEC_BLOCKSIZE)
|
||||
#define VERITY_TABLE_ARGS 10 /* mandatory arguments */
|
||||
#define VERITY_MIN_TABLE_SIZE (VERITY_TABLE_ARGS * 2) /* for a sanity check */
|
||||
#define VERITY_MAX_TABLE_SIZE (VERITY_METADATA_SIZE - sizeof(verity_header))
|
||||
|
||||
/* verity header and metadata */
|
||||
#define VERITY_MAGIC 0xB001B001
|
||||
#define VERITY_MAGIC_DISABLE 0x46464F56
|
||||
#define VERITY_VERSION 0
|
||||
#define VERITY_TABLE_FIELDS 10
|
||||
#define VERITY_TABLE_VERSION 1
|
||||
|
||||
struct verity_header {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint8_t signature[ANDROID_PUBKEY_MODULUS_SIZE];
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
/* file handle */
|
||||
struct ecc_info {
|
||||
bool valid;
|
||||
int roots;
|
||||
int rsn;
|
||||
uint32_t size;
|
||||
uint64_t blocks;
|
||||
uint64_t rounds;
|
||||
uint64_t start; /* offset in file */
|
||||
};
|
||||
|
||||
struct verity_info {
|
||||
bool disabled;
|
||||
char *table;
|
||||
uint32_t hash_data_blocks;
|
||||
uint32_t hash_size;
|
||||
uint64_t hash_data_offset;
|
||||
uint64_t hash_start;
|
||||
uint8_t *hash;
|
||||
uint32_t salt_size;
|
||||
uint8_t *salt;
|
||||
uint64_t data_blocks;
|
||||
uint64_t metadata_start; /* offset in file */
|
||||
uint8_t zero_hash[SHA256_DIGEST_LENGTH];
|
||||
verity_header header;
|
||||
verity_header ecc_header;
|
||||
};
|
||||
|
||||
struct verity_block_info {
|
||||
uint64_t index;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct fec_handle {
|
||||
ecc_info ecc;
|
||||
int fd;
|
||||
int flags; /* additional flags passed to fec_open */
|
||||
int mode; /* mode for open(2) */
|
||||
pthread_mutex_t mutex;
|
||||
uint64_t errors;
|
||||
uint64_t data_size;
|
||||
uint64_t pos;
|
||||
uint64_t size;
|
||||
verity_info verity;
|
||||
};
|
||||
|
||||
/* I/O helpers */
|
||||
extern bool raw_pread(fec_handle *f, void *buf, size_t count,
|
||||
uint64_t offset);
|
||||
extern bool raw_pwrite(fec_handle *f, const void *buf, size_t count,
|
||||
uint64_t offset);
|
||||
|
||||
/* processing functions */
|
||||
typedef ssize_t (*read_func)(fec_handle *f, uint8_t *dest, size_t count,
|
||||
uint64_t offset, size_t *errors);
|
||||
|
||||
extern ssize_t process(fec_handle *f, uint8_t *buf, size_t count,
|
||||
uint64_t offset, read_func func);
|
||||
|
||||
/* verity functions */
|
||||
extern uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
|
||||
uint32_t *level_hashes);
|
||||
|
||||
extern int verity_parse_header(fec_handle *f, uint64_t offset);
|
||||
|
||||
extern bool verity_check_block(fec_handle *f, const uint8_t *expected,
|
||||
const uint8_t *block);
|
||||
|
||||
/* helper macros */
|
||||
#ifndef unlikely
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#endif
|
||||
|
||||
#ifndef stringify
|
||||
#define __stringify(x) #x
|
||||
#define stringify(x) __stringify(x)
|
||||
#endif
|
||||
|
||||
/* warnings, errors, debug output */
|
||||
#ifdef FEC_NO_KLOG
|
||||
#define __log(func, type, format, args...) \
|
||||
fprintf(stderr, "fec: <%d> " type ": %s: " format "\n", \
|
||||
(int)syscall(SYS_gettid), __FUNCTION__, ##args)
|
||||
#else
|
||||
#include <cutils/klog.h>
|
||||
|
||||
#define __log(func, type, format, args...) \
|
||||
KLOG_##func("fec", "<%d> " type ": %s: " format "\n", \
|
||||
(int)syscall(SYS_gettid), __FUNCTION__, ##args)
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define debug(format, args...)
|
||||
#else
|
||||
#define debug(format, args...) __log(DEBUG, "debug", format, ##args)
|
||||
#endif
|
||||
|
||||
#define warn(format, args...) __log(WARNING, "warning", format, ##args)
|
||||
#define error(format, args...) __log(ERROR, "error", format, ##args)
|
||||
|
||||
#define check(p) \
|
||||
if (unlikely(!(p))) { \
|
||||
error("`%s' failed", #p); \
|
||||
errno = EFAULT; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#endif /* __FEC_PRIVATE_H__ */
|
139
android/system/extras/libfec/fec_process.cpp
Normal file
139
android/system/extras/libfec/fec_process.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 "fec_private.h"
|
||||
|
||||
struct process_info {
|
||||
int id;
|
||||
fec_handle *f;
|
||||
uint8_t *buf;
|
||||
size_t count;
|
||||
uint64_t offset;
|
||||
read_func func;
|
||||
ssize_t rc;
|
||||
size_t errors;
|
||||
};
|
||||
|
||||
/* thread function */
|
||||
static void * __process(void *cookie)
|
||||
{
|
||||
process_info *p = static_cast<process_info *>(cookie);
|
||||
|
||||
debug("thread %d: [%" PRIu64 ", %" PRIu64 ")", p->id, p->offset,
|
||||
p->offset + p->count);
|
||||
|
||||
p->rc = p->func(p->f, p->buf, p->count, p->offset, &p->errors);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* launches a maximum number of threads to process a read */
|
||||
ssize_t process(fec_handle *f, uint8_t *buf, size_t count, uint64_t offset,
|
||||
read_func func)
|
||||
{
|
||||
check(f);
|
||||
check(buf)
|
||||
check(func);
|
||||
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int threads = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
if (threads < WORK_MIN_THREADS) {
|
||||
threads = WORK_MIN_THREADS;
|
||||
} else if (threads > WORK_MAX_THREADS) {
|
||||
threads = WORK_MAX_THREADS;
|
||||
}
|
||||
|
||||
uint64_t start = (offset / FEC_BLOCKSIZE) * FEC_BLOCKSIZE;
|
||||
size_t blocks = fec_div_round_up(count, FEC_BLOCKSIZE);
|
||||
|
||||
size_t count_per_thread = fec_div_round_up(blocks, threads) * FEC_BLOCKSIZE;
|
||||
size_t max_threads = fec_div_round_up(count, count_per_thread);
|
||||
|
||||
if ((size_t)threads > max_threads) {
|
||||
threads = (int)max_threads;
|
||||
}
|
||||
|
||||
size_t left = count;
|
||||
uint64_t pos = offset;
|
||||
uint64_t end = start + count_per_thread;
|
||||
|
||||
debug("%d threads, %zu bytes per thread (total %zu)", threads,
|
||||
count_per_thread, count);
|
||||
|
||||
std::vector<pthread_t> handles;
|
||||
process_info info[threads];
|
||||
ssize_t rc = 0;
|
||||
|
||||
/* start threads to process queue */
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
check(left > 0);
|
||||
|
||||
info[i].id = i;
|
||||
info[i].f = f;
|
||||
info[i].buf = &buf[pos - offset];
|
||||
info[i].count = (size_t)(end - pos);
|
||||
info[i].offset = pos;
|
||||
info[i].func = func;
|
||||
info[i].rc = -1;
|
||||
info[i].errors = 0;
|
||||
|
||||
if (info[i].count > left) {
|
||||
info[i].count = left;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
if (pthread_create(&thread, NULL, __process, &info[i]) != 0) {
|
||||
error("failed to create thread: %s", strerror(errno));
|
||||
rc = -1;
|
||||
} else {
|
||||
handles.push_back(thread);
|
||||
}
|
||||
|
||||
pos = end;
|
||||
end += count_per_thread;
|
||||
left -= info[i].count;
|
||||
}
|
||||
|
||||
check(left == 0);
|
||||
|
||||
ssize_t nread = 0;
|
||||
|
||||
/* wait for all threads to complete */
|
||||
for (auto thread : handles) {
|
||||
process_info *p = NULL;
|
||||
|
||||
if (pthread_join(thread, (void **)&p) != 0) {
|
||||
error("failed to join thread: %s", strerror(errno));
|
||||
rc = -1;
|
||||
} else if (!p || p->rc == -1) {
|
||||
rc = -1;
|
||||
} else {
|
||||
nread += p->rc;
|
||||
f->errors += p->errors;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
557
android/system/extras/libfec/fec_read.cpp
Normal file
557
android/system/extras/libfec/fec_read.cpp
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
extern "C" {
|
||||
#include <fec.h>
|
||||
}
|
||||
|
||||
#include "fec_private.h"
|
||||
|
||||
using rs_unique_ptr = std::unique_ptr<void, decltype(&free_rs_char)>;
|
||||
|
||||
/* prints a hexdump of `data' using warn(...) */
|
||||
static void dump(const char *name, uint64_t value, const uint8_t *data,
|
||||
size_t size)
|
||||
{
|
||||
const int bytes_per_line = 16;
|
||||
char hex[bytes_per_line * 3 + 1];
|
||||
char prn[bytes_per_line + 1];
|
||||
|
||||
warn("%s (%" PRIu64 ") (%zu bytes):", name ? name : "", value, size);
|
||||
|
||||
if (!data) {
|
||||
warn(" (null)");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < size; n += bytes_per_line) {
|
||||
memset(hex, 0, sizeof(hex));
|
||||
memset(prn, 0, sizeof(prn));
|
||||
|
||||
for (size_t m = 0; m < bytes_per_line; ++m) {
|
||||
if (n + m < size) {
|
||||
ptrdiff_t offset = &hex[m * 3] - hex;
|
||||
snprintf(hex + offset, sizeof(hex) - offset, "%02x ",
|
||||
data[n + m]);
|
||||
|
||||
if (isprint(data[n + m])) {
|
||||
prn[m] = data[n + m];
|
||||
} else {
|
||||
prn[m] = '.';
|
||||
}
|
||||
} else {
|
||||
strcpy(&hex[m * 3], " ");
|
||||
}
|
||||
}
|
||||
|
||||
warn(" %04zu %s %s", n, hex, prn);
|
||||
}
|
||||
}
|
||||
|
||||
/* checks if `offset' is within a corrupted block */
|
||||
static inline bool is_erasure(fec_handle *f, uint64_t offset,
|
||||
const uint8_t *data)
|
||||
{
|
||||
if (unlikely(offset >= f->data_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ideally, we would like to know if a specific byte on this block has
|
||||
been corrupted, but knowing whether any of them is can be useful as
|
||||
well, because often the entire block is corrupted */
|
||||
|
||||
uint64_t n = offset / FEC_BLOCKSIZE;
|
||||
|
||||
return !verity_check_block(f, &f->verity.hash[n * SHA256_DIGEST_LENGTH],
|
||||
data);
|
||||
}
|
||||
|
||||
/* check if `offset' is within a block expected to contain zeros */
|
||||
static inline bool is_zero(fec_handle *f, uint64_t offset)
|
||||
{
|
||||
verity_info *v = &f->verity;
|
||||
|
||||
if (!v->hash || unlikely(offset >= f->data_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t hash_offset = (offset / FEC_BLOCKSIZE) * SHA256_DIGEST_LENGTH;
|
||||
|
||||
if (unlikely(hash_offset >
|
||||
v->hash_data_blocks * FEC_BLOCKSIZE - SHA256_DIGEST_LENGTH)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !memcmp(v->zero_hash, &v->hash[hash_offset], SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
/* reads and decodes a single block starting from `offset', returns the number
|
||||
of bytes corrected in `errors' */
|
||||
static int __ecc_read(fec_handle *f, void *rs, uint8_t *dest, uint64_t offset,
|
||||
bool use_erasures, uint8_t *ecc_data, size_t *errors)
|
||||
{
|
||||
check(offset % FEC_BLOCKSIZE == 0);
|
||||
ecc_info *e = &f->ecc;
|
||||
|
||||
/* reverse interleaving: calculate the RS block that includes the requested
|
||||
offset */
|
||||
uint64_t rsb = offset - (offset / (e->rounds * FEC_BLOCKSIZE)) *
|
||||
e->rounds * FEC_BLOCKSIZE;
|
||||
int data_index = -1;
|
||||
int erasures[e->rsn];
|
||||
int neras = 0;
|
||||
|
||||
/* verity is required to check for erasures */
|
||||
check(!use_erasures || f->verity.hash);
|
||||
|
||||
for (int i = 0; i < e->rsn; ++i) {
|
||||
uint64_t interleaved = fec_ecc_interleave(rsb * e->rsn + i, e->rsn,
|
||||
e->rounds);
|
||||
|
||||
if (interleaved == offset) {
|
||||
data_index = i;
|
||||
}
|
||||
|
||||
/* to improve our chances of correcting IO errors, initialize the
|
||||
buffer to zeros even if we are going to read to it later */
|
||||
uint8_t bbuf[FEC_BLOCKSIZE] = {0};
|
||||
|
||||
if (likely(interleaved < e->start) && !is_zero(f, interleaved)) {
|
||||
/* copy raw data to reconstruct the RS block */
|
||||
if (!raw_pread(f, bbuf, FEC_BLOCKSIZE, interleaved)) {
|
||||
warn("failed to read: %s", strerror(errno));
|
||||
|
||||
/* treat errors as corruption */
|
||||
if (use_erasures && neras <= e->roots) {
|
||||
erasures[neras++] = i;
|
||||
}
|
||||
} else if (use_erasures && neras <= e->roots &&
|
||||
is_erasure(f, interleaved, bbuf)) {
|
||||
erasures[neras++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < FEC_BLOCKSIZE; ++j) {
|
||||
ecc_data[j * FEC_RSM + i] = bbuf[j];
|
||||
}
|
||||
}
|
||||
|
||||
check(data_index >= 0);
|
||||
|
||||
size_t nerrs = 0;
|
||||
uint8_t copy[FEC_RSM];
|
||||
|
||||
for (int i = 0; i < FEC_BLOCKSIZE; ++i) {
|
||||
/* copy parity data */
|
||||
if (!raw_pread(f, &ecc_data[i * FEC_RSM + e->rsn], e->roots,
|
||||
e->start + (i + rsb) * e->roots)) {
|
||||
error("failed to read ecc data: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* for debugging decoding failures, because decode_rs_char can mangle
|
||||
ecc_data */
|
||||
if (unlikely(use_erasures)) {
|
||||
memcpy(copy, &ecc_data[i * FEC_RSM], FEC_RSM);
|
||||
}
|
||||
|
||||
/* decode */
|
||||
int rc = decode_rs_char(rs, &ecc_data[i * FEC_RSM], erasures, neras);
|
||||
|
||||
if (unlikely(rc < 0)) {
|
||||
if (use_erasures) {
|
||||
error("RS block %" PRIu64 ": decoding failed (%d erasures)",
|
||||
rsb, neras);
|
||||
dump("raw RS block", rsb, copy, FEC_RSM);
|
||||
} else if (!f->verity.hash) {
|
||||
warn("RS block %" PRIu64 ": decoding failed", rsb);
|
||||
} else {
|
||||
debug("RS block %" PRIu64 ": decoding failed", rsb);
|
||||
}
|
||||
|
||||
errno = EIO;
|
||||
return -1;
|
||||
} else if (unlikely(rc > 0)) {
|
||||
check(rc <= (use_erasures ? e->roots : e->roots / 2));
|
||||
nerrs += rc;
|
||||
}
|
||||
|
||||
dest[i] = ecc_data[i * FEC_RSM + data_index];
|
||||
}
|
||||
|
||||
if (nerrs) {
|
||||
warn("RS block %" PRIu64 ": corrected %zu errors", rsb, nerrs);
|
||||
*errors += nerrs;
|
||||
}
|
||||
|
||||
return FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
/* initializes RS decoder and allocates memory for interleaving */
|
||||
static int ecc_init(fec_handle *f, rs_unique_ptr& rs,
|
||||
std::unique_ptr<uint8_t[]>& ecc_data)
|
||||
{
|
||||
check(f);
|
||||
|
||||
rs.reset(init_rs_char(FEC_PARAMS(f->ecc.roots)));
|
||||
|
||||
if (unlikely(!rs)) {
|
||||
error("failed to initialize RS");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ecc_data.reset(new (std::nothrow) uint8_t[FEC_RSM * FEC_BLOCKSIZE]);
|
||||
|
||||
if (unlikely(!ecc_data)) {
|
||||
error("failed to allocate ecc buffer");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reads `count' bytes from `offset' and corrects possible errors without
|
||||
erasure detection, returning the number of corrected bytes in `errors' */
|
||||
static ssize_t ecc_read(fec_handle *f, uint8_t *dest, size_t count,
|
||||
uint64_t offset, size_t *errors)
|
||||
{
|
||||
check(f);
|
||||
check(dest);
|
||||
check(offset < f->data_size);
|
||||
check(offset + count <= f->data_size);
|
||||
check(errors);
|
||||
|
||||
debug("[%" PRIu64 ", %" PRIu64 ")", offset, offset + count);
|
||||
|
||||
rs_unique_ptr rs(NULL, free_rs_char);
|
||||
std::unique_ptr<uint8_t[]> ecc_data;
|
||||
|
||||
if (ecc_init(f, rs, ecc_data) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t curr = offset / FEC_BLOCKSIZE;
|
||||
size_t coff = (size_t)(offset - curr * FEC_BLOCKSIZE);
|
||||
size_t left = count;
|
||||
|
||||
uint8_t data[FEC_BLOCKSIZE];
|
||||
|
||||
while (left > 0) {
|
||||
/* there's no erasure detection without verity metadata */
|
||||
if (__ecc_read(f, rs.get(), data, curr * FEC_BLOCKSIZE, false,
|
||||
ecc_data.get(), errors) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t copy = FEC_BLOCKSIZE - coff;
|
||||
|
||||
if (copy > left) {
|
||||
copy = left;
|
||||
}
|
||||
|
||||
memcpy(dest, &data[coff], copy);
|
||||
|
||||
dest += copy;
|
||||
left -= copy;
|
||||
coff = 0;
|
||||
++curr;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* reads `count' bytes from `offset', corrects possible errors with
|
||||
erasure detection, and verifies the integrity of read data using
|
||||
verity hash tree; returns the number of corrections in `errors' */
|
||||
static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
|
||||
uint64_t offset, size_t *errors)
|
||||
{
|
||||
check(f);
|
||||
check(dest);
|
||||
check(offset < f->data_size);
|
||||
check(offset + count <= f->data_size);
|
||||
check(f->verity.hash);
|
||||
check(errors);
|
||||
|
||||
debug("[%" PRIu64 ", %" PRIu64 ")", offset, offset + count);
|
||||
|
||||
rs_unique_ptr rs(NULL, free_rs_char);
|
||||
std::unique_ptr<uint8_t[]> ecc_data;
|
||||
|
||||
if (f->ecc.start && ecc_init(f, rs, ecc_data) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t curr = offset / FEC_BLOCKSIZE;
|
||||
size_t coff = (size_t)(offset - curr * FEC_BLOCKSIZE);
|
||||
size_t left = count;
|
||||
uint8_t data[FEC_BLOCKSIZE];
|
||||
|
||||
uint64_t max_hash_block = (f->verity.hash_data_blocks * FEC_BLOCKSIZE -
|
||||
SHA256_DIGEST_LENGTH) / SHA256_DIGEST_LENGTH;
|
||||
|
||||
while (left > 0) {
|
||||
check(curr <= max_hash_block);
|
||||
|
||||
uint8_t *hash = &f->verity.hash[curr * SHA256_DIGEST_LENGTH];
|
||||
uint64_t curr_offset = curr * FEC_BLOCKSIZE;
|
||||
|
||||
bool expect_zeros = is_zero(f, curr_offset);
|
||||
|
||||
/* if we are in read-only mode and expect to read a zero block,
|
||||
skip reading and just return zeros */
|
||||
if (f->mode & O_RDONLY && expect_zeros) {
|
||||
memset(data, 0, FEC_BLOCKSIZE);
|
||||
goto valid;
|
||||
}
|
||||
|
||||
/* copy raw data without error correction */
|
||||
if (!raw_pread(f, data, FEC_BLOCKSIZE, curr_offset)) {
|
||||
error("failed to read: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (likely(verity_check_block(f, hash, data))) {
|
||||
goto valid;
|
||||
}
|
||||
|
||||
/* we know the block is supposed to contain zeros, so return zeros
|
||||
instead of trying to correct it */
|
||||
if (expect_zeros) {
|
||||
memset(data, 0, FEC_BLOCKSIZE);
|
||||
goto corrected;
|
||||
}
|
||||
|
||||
if (!f->ecc.start) {
|
||||
/* fatal error without ecc */
|
||||
error("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64,
|
||||
offset, offset + count, curr);
|
||||
return -1;
|
||||
} else {
|
||||
debug("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64,
|
||||
offset, offset + count, curr);
|
||||
}
|
||||
|
||||
/* try to correct without erasures first, because checking for
|
||||
erasure locations is slower */
|
||||
if (__ecc_read(f, rs.get(), data, curr_offset, false, ecc_data.get(),
|
||||
errors) == FEC_BLOCKSIZE &&
|
||||
verity_check_block(f, hash, data)) {
|
||||
goto corrected;
|
||||
}
|
||||
|
||||
/* try to correct with erasures */
|
||||
if (__ecc_read(f, rs.get(), data, curr_offset, true, ecc_data.get(),
|
||||
errors) == FEC_BLOCKSIZE &&
|
||||
verity_check_block(f, hash, data)) {
|
||||
goto corrected;
|
||||
}
|
||||
|
||||
error("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64
|
||||
" (offset %" PRIu64 ") cannot be recovered",
|
||||
offset, offset + count, curr, curr_offset);
|
||||
dump("decoded block", curr, data, FEC_BLOCKSIZE);
|
||||
|
||||
errno = EIO;
|
||||
return -1;
|
||||
|
||||
corrected:
|
||||
/* update the corrected block to the file if we are in r/w mode */
|
||||
if (f->mode & O_RDWR &&
|
||||
!raw_pwrite(f, data, FEC_BLOCKSIZE, curr_offset)) {
|
||||
error("failed to write: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
valid:
|
||||
size_t copy = FEC_BLOCKSIZE - coff;
|
||||
|
||||
if (copy > left) {
|
||||
copy = left;
|
||||
}
|
||||
|
||||
memcpy(dest, &data[coff], copy);
|
||||
|
||||
dest += copy;
|
||||
left -= copy;
|
||||
coff = 0;
|
||||
++curr;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* sets the internal file position to `offset' relative to `whence' */
|
||||
int fec_seek(struct fec_handle *f, int64_t offset, int whence)
|
||||
{
|
||||
check(f);
|
||||
|
||||
if (whence == SEEK_SET) {
|
||||
if (offset < 0) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->pos = offset;
|
||||
} else if (whence == SEEK_CUR) {
|
||||
if (offset < 0 && f->pos < (uint64_t)-offset) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
} else if (offset > 0 && (uint64_t)offset > UINT64_MAX - f->pos) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->pos += offset;
|
||||
} else if (whence == SEEK_END) {
|
||||
if (offset >= 0) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
} else if ((uint64_t)-offset > f->size) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->pos = f->size + offset;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reads up to `count' bytes starting from the internal file position using
|
||||
error correction and integrity validation, if available */
|
||||
ssize_t fec_read(struct fec_handle *f, void *buf, size_t count)
|
||||
{
|
||||
ssize_t rc = fec_pread(f, buf, count, f->pos);
|
||||
|
||||
if (rc > 0) {
|
||||
check(f->pos < UINT64_MAX - rc);
|
||||
f->pos += rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* for a file with size `max', returns the number of bytes we can read starting
|
||||
from `offset', up to `count' bytes */
|
||||
static inline size_t get_max_count(uint64_t offset, size_t count, uint64_t max)
|
||||
{
|
||||
if (offset >= max) {
|
||||
return 0;
|
||||
} else if (offset > max - count) {
|
||||
return (size_t)(max - offset);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* reads `count' bytes from `f->fd' starting from `offset', and copies the
|
||||
data to `buf' */
|
||||
bool raw_pread(fec_handle *f, void *buf, size_t count, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(buf);
|
||||
|
||||
uint8_t *p = (uint8_t *)buf;
|
||||
size_t remaining = count;
|
||||
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(pread64(f->fd, p, remaining, offset));
|
||||
|
||||
if (n <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p += n;
|
||||
remaining -= n;
|
||||
offset += n;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* writes `count' bytes from `buf' to `f->fd' to a file position `offset' */
|
||||
bool raw_pwrite(fec_handle *f, const void *buf, size_t count, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(buf);
|
||||
|
||||
const uint8_t *p = (const uint8_t *)buf;
|
||||
size_t remaining = count;
|
||||
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(pwrite64(f->fd, p, remaining, offset));
|
||||
|
||||
if (n <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p += n;
|
||||
remaining -= n;
|
||||
offset += n;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* reads up to `count' bytes starting from `offset' using error correction and
|
||||
integrity validation, if available */
|
||||
ssize_t fec_pread(struct fec_handle *f, void *buf, size_t count,
|
||||
uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(buf);
|
||||
|
||||
if (unlikely(offset > UINT64_MAX - count)) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (f->verity.hash) {
|
||||
return process(f, (uint8_t *)buf,
|
||||
get_max_count(offset, count, f->data_size), offset,
|
||||
verity_read);
|
||||
} else if (f->ecc.start) {
|
||||
check(f->ecc.start < f->size);
|
||||
|
||||
count = get_max_count(offset, count, f->data_size);
|
||||
ssize_t rc = process(f, (uint8_t *)buf, count, offset, ecc_read);
|
||||
|
||||
if (rc >= 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* return raw data if pure ecc read fails; due to interleaving
|
||||
the specific blocks the caller wants may still be fine */
|
||||
} else {
|
||||
count = get_max_count(offset, count, f->size);
|
||||
}
|
||||
|
||||
if (raw_pread(f, buf, count, offset)) {
|
||||
return count;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
671
android/system/extras/libfec/fec_verity.cpp
Normal file
671
android/system/extras/libfec/fec_verity.cpp
Normal file
|
@ -0,0 +1,671 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <android-base/strings.h>
|
||||
#include "fec_private.h"
|
||||
|
||||
/* converts a hex nibble into an int */
|
||||
static inline int hextobin(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* converts a hex string `src' of `size' characters to binary and copies the
|
||||
the result into `dst' */
|
||||
static int parse_hex(uint8_t *dst, uint32_t size, const char *src)
|
||||
{
|
||||
int l, h;
|
||||
|
||||
check(dst);
|
||||
check(src);
|
||||
check(2 * size == strlen(src));
|
||||
|
||||
while (size) {
|
||||
h = hextobin(tolower(*src++));
|
||||
l = hextobin(tolower(*src++));
|
||||
|
||||
check(l >= 0);
|
||||
check(h >= 0);
|
||||
|
||||
*dst++ = (h << 4) | l;
|
||||
--size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parses a 64-bit unsigned integer from string `src' into `dst' and if
|
||||
`maxval' is >0, checks that `dst' <= `maxval' */
|
||||
static int parse_uint64(const char *src, uint64_t maxval, uint64_t *dst)
|
||||
{
|
||||
char *end;
|
||||
unsigned long long int value;
|
||||
|
||||
check(src);
|
||||
check(dst);
|
||||
|
||||
errno = 0;
|
||||
value = strtoull(src, &end, 0);
|
||||
|
||||
if (*src == '\0' || *end != '\0' ||
|
||||
(errno == ERANGE && value == ULLONG_MAX)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (maxval && value > maxval) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*dst = (uint64_t)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* computes the size of verity hash tree for `file_size' bytes and returns the
|
||||
number of hash tree levels in `verity_levels,' and the number of hashes per
|
||||
level in `level_hashes', if the parameters are non-NULL */
|
||||
uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
|
||||
uint32_t *level_hashes)
|
||||
{
|
||||
/* we assume a known metadata size, 4 KiB block size, and SHA-256 to avoid
|
||||
relying on disk content */
|
||||
|
||||
uint32_t level = 0;
|
||||
uint64_t total = 0;
|
||||
uint64_t hashes = file_size / FEC_BLOCKSIZE;
|
||||
|
||||
do {
|
||||
if (level_hashes) {
|
||||
level_hashes[level] = hashes;
|
||||
}
|
||||
|
||||
hashes = fec_div_round_up(hashes * SHA256_DIGEST_LENGTH, FEC_BLOCKSIZE);
|
||||
total += hashes;
|
||||
|
||||
++level;
|
||||
} while (hashes > 1);
|
||||
|
||||
if (verity_levels) {
|
||||
*verity_levels = level;
|
||||
}
|
||||
|
||||
return total * FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
/* computes a SHA-256 salted with `f->verity.salt' from a FEC_BLOCKSIZE byte
|
||||
buffer `block', and copies the hash to `hash' */
|
||||
static inline int verity_hash(fec_handle *f, const uint8_t *block,
|
||||
uint8_t *hash)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
|
||||
check(f);
|
||||
check(f->verity.salt);
|
||||
SHA256_Update(&ctx, f->verity.salt, f->verity.salt_size);
|
||||
|
||||
check(block);
|
||||
SHA256_Update(&ctx, block, FEC_BLOCKSIZE);
|
||||
|
||||
check(hash);
|
||||
SHA256_Final(hash, &ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* computes a verity hash for FEC_BLOCKSIZE bytes from buffer `block' and
|
||||
compares it to the expected value in `expected' */
|
||||
bool verity_check_block(fec_handle *f, const uint8_t *expected,
|
||||
const uint8_t *block)
|
||||
{
|
||||
check(f);
|
||||
check(block);
|
||||
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
if (unlikely(verity_hash(f, block, hash) == -1)) {
|
||||
error("failed to hash");
|
||||
return false;
|
||||
}
|
||||
|
||||
check(expected);
|
||||
return !memcmp(expected, hash, SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
/* reads a verity hash and the corresponding data block using error correction,
|
||||
if available */
|
||||
static bool ecc_read_hashes(fec_handle *f, uint64_t hash_offset,
|
||||
uint8_t *hash, uint64_t data_offset, uint8_t *data)
|
||||
{
|
||||
check(f);
|
||||
|
||||
if (hash && fec_pread(f, hash, SHA256_DIGEST_LENGTH, hash_offset) !=
|
||||
SHA256_DIGEST_LENGTH) {
|
||||
error("failed to read hash tree: offset %" PRIu64 ": %s", hash_offset,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
check(data);
|
||||
|
||||
if (fec_pread(f, data, FEC_BLOCKSIZE, data_offset) != FEC_BLOCKSIZE) {
|
||||
error("failed to read hash tree: data_offset %" PRIu64 ": %s",
|
||||
data_offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* reads the verity hash tree, validates it against the root hash in `root',
|
||||
corrects errors if necessary, and copies valid data blocks for later use
|
||||
to `f->verity.hash' */
|
||||
static int verify_tree(fec_handle *f, const uint8_t *root)
|
||||
{
|
||||
uint8_t data[FEC_BLOCKSIZE];
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
check(f);
|
||||
check(root);
|
||||
|
||||
verity_info *v = &f->verity;
|
||||
uint32_t levels = 0;
|
||||
|
||||
/* calculate the size and the number of levels in the hash tree */
|
||||
v->hash_size =
|
||||
verity_get_size(v->data_blocks * FEC_BLOCKSIZE, &levels, NULL);
|
||||
|
||||
check(v->hash_start < UINT64_MAX - v->hash_size);
|
||||
check(v->hash_start + v->hash_size <= f->data_size);
|
||||
|
||||
uint64_t hash_offset = v->hash_start;
|
||||
uint64_t data_offset = hash_offset + FEC_BLOCKSIZE;
|
||||
|
||||
v->hash_data_offset = data_offset;
|
||||
|
||||
/* validate the root hash */
|
||||
if (!raw_pread(f, data, FEC_BLOCKSIZE, hash_offset) ||
|
||||
!verity_check_block(f, root, data)) {
|
||||
/* try to correct */
|
||||
if (!ecc_read_hashes(f, 0, NULL, hash_offset, data) ||
|
||||
!verity_check_block(f, root, data)) {
|
||||
error("root hash invalid");
|
||||
return -1;
|
||||
} else if (f->mode & O_RDWR &&
|
||||
!raw_pwrite(f, data, FEC_BLOCKSIZE, hash_offset)) {
|
||||
error("failed to rewrite the root block: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
debug("root hash valid");
|
||||
|
||||
/* calculate the number of hashes on each level */
|
||||
uint32_t hashes[levels];
|
||||
|
||||
verity_get_size(v->data_blocks * FEC_BLOCKSIZE, NULL, hashes);
|
||||
|
||||
/* calculate the size and offset for the data hashes */
|
||||
for (uint32_t i = 1; i < levels; ++i) {
|
||||
uint32_t blocks = hashes[levels - i];
|
||||
debug("%u hash blocks on level %u", blocks, levels - i);
|
||||
|
||||
v->hash_data_offset = data_offset;
|
||||
v->hash_data_blocks = blocks;
|
||||
|
||||
data_offset += blocks * FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
check(v->hash_data_blocks);
|
||||
check(v->hash_data_blocks <= v->hash_size / FEC_BLOCKSIZE);
|
||||
|
||||
check(v->hash_data_offset);
|
||||
check(v->hash_data_offset <=
|
||||
UINT64_MAX - (v->hash_data_blocks * FEC_BLOCKSIZE));
|
||||
check(v->hash_data_offset < f->data_size);
|
||||
check(v->hash_data_offset + v->hash_data_blocks * FEC_BLOCKSIZE <=
|
||||
f->data_size);
|
||||
|
||||
/* copy data hashes to memory in case they are corrupted, so we don't
|
||||
have to correct them every time they are needed */
|
||||
std::unique_ptr<uint8_t[]> data_hashes(
|
||||
new (std::nothrow) uint8_t[f->verity.hash_data_blocks * FEC_BLOCKSIZE]);
|
||||
|
||||
if (!data_hashes) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* validate the rest of the hash tree */
|
||||
data_offset = hash_offset + FEC_BLOCKSIZE;
|
||||
|
||||
for (uint32_t i = 1; i < levels; ++i) {
|
||||
uint32_t blocks = hashes[levels - i];
|
||||
|
||||
for (uint32_t j = 0; j < blocks; ++j) {
|
||||
/* ecc reads are very I/O intensive, so read raw hash tree and do
|
||||
error correcting only if it doesn't validate */
|
||||
if (!raw_pread(f, hash, SHA256_DIGEST_LENGTH,
|
||||
hash_offset + j * SHA256_DIGEST_LENGTH) ||
|
||||
!raw_pread(f, data, FEC_BLOCKSIZE,
|
||||
data_offset + j * FEC_BLOCKSIZE)) {
|
||||
error("failed to read hashes: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!verity_check_block(f, hash, data)) {
|
||||
/* try to correct */
|
||||
if (!ecc_read_hashes(f,
|
||||
hash_offset + j * SHA256_DIGEST_LENGTH, hash,
|
||||
data_offset + j * FEC_BLOCKSIZE, data) ||
|
||||
!verity_check_block(f, hash, data)) {
|
||||
error("invalid hash tree: hash_offset %" PRIu64 ", "
|
||||
"data_offset %" PRIu64 ", block %u",
|
||||
hash_offset, data_offset, j);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* update the corrected blocks to the file if we are in r/w
|
||||
mode */
|
||||
if (f->mode & O_RDWR) {
|
||||
if (!raw_pwrite(f, hash, SHA256_DIGEST_LENGTH,
|
||||
hash_offset + j * SHA256_DIGEST_LENGTH) ||
|
||||
!raw_pwrite(f, data, FEC_BLOCKSIZE,
|
||||
data_offset + j * FEC_BLOCKSIZE)) {
|
||||
error("failed to write hashes: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks == v->hash_data_blocks) {
|
||||
memcpy(data_hashes.get() + j * FEC_BLOCKSIZE, data,
|
||||
FEC_BLOCKSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
hash_offset = data_offset;
|
||||
data_offset += blocks * FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
debug("valid");
|
||||
|
||||
if (v->hash) {
|
||||
delete[] v->hash;
|
||||
v->hash = NULL;
|
||||
}
|
||||
|
||||
v->hash = data_hashes.release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reads, corrects and parses the verity table, validates parameters, and if
|
||||
`f->flags' does not have `FEC_VERITY_DISABLE' set, calls `verify_tree' to
|
||||
load and validate the hash tree */
|
||||
static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useecc)
|
||||
{
|
||||
check(f);
|
||||
check(size >= VERITY_MIN_TABLE_SIZE);
|
||||
check(size <= VERITY_MAX_TABLE_SIZE);
|
||||
|
||||
debug("offset = %" PRIu64 ", size = %u", offset, size);
|
||||
|
||||
verity_info *v = &f->verity;
|
||||
std::unique_ptr<char[]> table(new (std::nothrow) char[size + 1]);
|
||||
|
||||
if (!table) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!useecc) {
|
||||
if (!raw_pread(f, table.get(), size, offset)) {
|
||||
error("failed to read verity table: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (fec_pread(f, table.get(), size, offset) != (ssize_t)size) {
|
||||
error("failed to ecc read verity table: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
table[size] = '\0';
|
||||
debug("verity table: '%s'", table.get());
|
||||
|
||||
int i = 0;
|
||||
std::unique_ptr<uint8_t[]> salt;
|
||||
uint8_t root[SHA256_DIGEST_LENGTH];
|
||||
|
||||
auto tokens = android::base::Split(table.get(), " ");
|
||||
|
||||
for (const auto& token : tokens) {
|
||||
switch (i++) {
|
||||
case 0: /* version */
|
||||
if (token != stringify(VERITY_TABLE_VERSION)) {
|
||||
error("unsupported verity table version: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 3: /* data_block_size */
|
||||
case 4: /* hash_block_size */
|
||||
/* assume 4 KiB block sizes for everything */
|
||||
if (token != stringify(FEC_BLOCKSIZE)) {
|
||||
error("unsupported verity block size: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 5: /* num_data_blocks */
|
||||
if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
|
||||
&v->data_blocks) == -1) {
|
||||
error("invalid number of verity data blocks: %s",
|
||||
token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 6: /* hash_start_block */
|
||||
if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
|
||||
&v->hash_start) == -1) {
|
||||
error("invalid verity hash start block: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
v->hash_start *= FEC_BLOCKSIZE;
|
||||
break;
|
||||
case 7: /* algorithm */
|
||||
if (token != "sha256") {
|
||||
error("unsupported verity hash algorithm: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 8: /* digest */
|
||||
if (parse_hex(root, sizeof(root), token.c_str()) == -1) {
|
||||
error("invalid verity root hash: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 9: /* salt */
|
||||
v->salt_size = token.size();
|
||||
check(v->salt_size % 2 == 0);
|
||||
v->salt_size /= 2;
|
||||
|
||||
salt.reset(new (std::nothrow) uint8_t[v->salt_size]);
|
||||
|
||||
if (!salt) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parse_hex(salt.get(), v->salt_size, token.c_str()) == -1) {
|
||||
error("invalid verity salt: %s", token.c_str());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < VERITY_TABLE_ARGS) {
|
||||
error("not enough arguments in verity table: %d; expected at least "
|
||||
stringify(VERITY_TABLE_ARGS), i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
check(v->hash_start < f->data_size);
|
||||
|
||||
if (v->metadata_start < v->hash_start) {
|
||||
check(v->data_blocks == v->metadata_start / FEC_BLOCKSIZE);
|
||||
} else {
|
||||
check(v->data_blocks == v->hash_start / FEC_BLOCKSIZE);
|
||||
}
|
||||
|
||||
if (v->salt) {
|
||||
delete[] v->salt;
|
||||
v->salt = NULL;
|
||||
}
|
||||
|
||||
v->salt = salt.release();
|
||||
|
||||
if (v->table) {
|
||||
delete[] v->table;
|
||||
v->table = NULL;
|
||||
}
|
||||
|
||||
v->table = table.release();
|
||||
|
||||
if (!(f->flags & FEC_VERITY_DISABLE)) {
|
||||
if (verify_tree(f, root) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
check(v->hash);
|
||||
|
||||
uint8_t zero_block[FEC_BLOCKSIZE];
|
||||
memset(zero_block, 0, FEC_BLOCKSIZE);
|
||||
|
||||
if (verity_hash(f, zero_block, v->zero_hash) == -1) {
|
||||
error("failed to hash");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rewrites verity metadata block using error corrected data in `f->verity' */
|
||||
static int rewrite_metadata(fec_handle *f, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(f->data_size > VERITY_METADATA_SIZE);
|
||||
check(offset <= f->data_size - VERITY_METADATA_SIZE);
|
||||
|
||||
std::unique_ptr<uint8_t[]> metadata(
|
||||
new (std::nothrow) uint8_t[VERITY_METADATA_SIZE]);
|
||||
|
||||
if (!metadata) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(metadata.get(), 0, VERITY_METADATA_SIZE);
|
||||
|
||||
verity_info *v = &f->verity;
|
||||
memcpy(metadata.get(), &v->header, sizeof(v->header));
|
||||
|
||||
check(v->table);
|
||||
size_t len = strlen(v->table);
|
||||
|
||||
check(sizeof(v->header) + len <= VERITY_METADATA_SIZE);
|
||||
memcpy(metadata.get() + sizeof(v->header), v->table, len);
|
||||
|
||||
return raw_pwrite(f, metadata.get(), VERITY_METADATA_SIZE, offset);
|
||||
}
|
||||
|
||||
static int validate_header(const fec_handle *f, const verity_header *header,
|
||||
uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(header);
|
||||
|
||||
if (header->magic != VERITY_MAGIC &&
|
||||
header->magic != VERITY_MAGIC_DISABLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->version != VERITY_VERSION) {
|
||||
error("unsupported verity version %u", header->version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->length < VERITY_MIN_TABLE_SIZE ||
|
||||
header->length > VERITY_MAX_TABLE_SIZE) {
|
||||
error("invalid verity table size: %u; expected ["
|
||||
stringify(VERITY_MIN_TABLE_SIZE) ", "
|
||||
stringify(VERITY_MAX_TABLE_SIZE) ")", header->length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* signature is skipped, because for our purposes it won't matter from
|
||||
where the data originates; the caller of the library is responsible
|
||||
for signature verification */
|
||||
|
||||
if (offset > UINT64_MAX - header->length) {
|
||||
error("invalid verity table length: %u", header->length);
|
||||
return -1;
|
||||
} else if (offset + header->length >= f->data_size) {
|
||||
error("invalid verity table length: %u", header->length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* attempts to read verity metadata from `f->fd' position `offset'; if in r/w
|
||||
mode, rewrites the metadata if it had errors */
|
||||
int verity_parse_header(fec_handle *f, uint64_t offset)
|
||||
{
|
||||
check(f);
|
||||
check(f->data_size > VERITY_METADATA_SIZE);
|
||||
|
||||
if (offset > f->data_size - VERITY_METADATA_SIZE) {
|
||||
debug("failed to read verity header: offset %" PRIu64 " is too far",
|
||||
offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
verity_info *v = &f->verity;
|
||||
uint64_t errors = f->errors;
|
||||
|
||||
if (!raw_pread(f, &v->header, sizeof(v->header), offset)) {
|
||||
error("failed to read verity header: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* use raw data to check for the alternative magic, because it will
|
||||
be error corrected to VERITY_MAGIC otherwise */
|
||||
if (v->header.magic == VERITY_MAGIC_DISABLE) {
|
||||
/* this value is not used by us, but can be used by a caller to
|
||||
decide whether dm-verity should be enabled */
|
||||
v->disabled = true;
|
||||
}
|
||||
|
||||
if (fec_pread(f, &v->ecc_header, sizeof(v->ecc_header), offset) !=
|
||||
sizeof(v->ecc_header)) {
|
||||
warn("failed to read verity header: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (validate_header(f, &v->header, offset)) {
|
||||
/* raw verity header is invalid; this could be due to corruption, or
|
||||
due to missing verity metadata */
|
||||
|
||||
if (validate_header(f, &v->ecc_header, offset)) {
|
||||
return -1; /* either way, we cannot recover */
|
||||
}
|
||||
|
||||
/* report mismatching fields */
|
||||
if (!v->disabled && v->header.magic != v->ecc_header.magic) {
|
||||
warn("corrected verity header magic");
|
||||
v->header.magic = v->ecc_header.magic;
|
||||
}
|
||||
|
||||
if (v->header.version != v->ecc_header.version) {
|
||||
warn("corrected verity header version");
|
||||
v->header.version = v->ecc_header.version;
|
||||
}
|
||||
|
||||
if (v->header.length != v->ecc_header.length) {
|
||||
warn("corrected verity header length");
|
||||
v->header.length = v->ecc_header.length;
|
||||
}
|
||||
|
||||
if (memcmp(v->header.signature, v->ecc_header.signature,
|
||||
sizeof(v->header.signature))) {
|
||||
warn("corrected verity header signature");
|
||||
/* we have no way of knowing which signature is correct, if either
|
||||
of them is */
|
||||
}
|
||||
}
|
||||
|
||||
v->metadata_start = offset;
|
||||
|
||||
if (parse_table(f, offset + sizeof(v->header), v->header.length,
|
||||
false) == -1 &&
|
||||
parse_table(f, offset + sizeof(v->header), v->header.length,
|
||||
true) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if we corrected something while parsing metadata and we are in r/w
|
||||
mode, rewrite the corrected metadata */
|
||||
if (f->mode & O_RDWR && f->errors > errors &&
|
||||
rewrite_metadata(f, offset) < 0) {
|
||||
warn("failed to rewrite verity metadata: %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (v->metadata_start < v->hash_start) {
|
||||
f->data_size = v->metadata_start;
|
||||
} else {
|
||||
f->data_size = v->hash_start;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fec_verity_set_status(struct fec_handle *f, bool enabled)
|
||||
{
|
||||
check(f);
|
||||
|
||||
if (!(f->mode & O_RDWR)) {
|
||||
error("cannot update verity magic: read-only handle");
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
verity_info *v = &f->verity;
|
||||
|
||||
if (!v->metadata_start) {
|
||||
error("cannot update verity magic: no metadata found");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (v->disabled == !enabled) {
|
||||
return 0; /* nothing to do */
|
||||
}
|
||||
|
||||
uint32_t magic = enabled ? VERITY_MAGIC : VERITY_MAGIC_DISABLE;
|
||||
|
||||
if (!raw_pwrite(f, &magic, sizeof(magic), v->metadata_start)) {
|
||||
error("failed to update verity magic to %08x: %s", magic,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
warn("updated verity magic to %08x (%s)", magic,
|
||||
enabled ? "enabled" : "disabled");
|
||||
v->disabled = !enabled;
|
||||
|
||||
return 0;
|
||||
}
|
70
android/system/extras/libfec/include/fec/ecc.h
Normal file
70
android/system/extras/libfec/include/fec/ecc.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#ifndef ___FEC_ECC_H___
|
||||
#define ___FEC_ECC_H___
|
||||
|
||||
#include <fec/io.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ecc parameters */
|
||||
#define FEC_RSM 255
|
||||
|
||||
/* parameters to init_rs_char */
|
||||
#define FEC_PARAMS(roots) \
|
||||
8, /* symbol size in bits */ \
|
||||
0x11d, /* field generator polynomial coefficients */ \
|
||||
0, /* first root of the generator */ \
|
||||
1, /* primitive element to generate polynomial roots */ \
|
||||
(roots), /* polynomial degree (number of roots) */ \
|
||||
0 /* padding bytes at the front of shortened block */
|
||||
|
||||
/* computes ceil(x / y) */
|
||||
inline uint64_t fec_div_round_up(uint64_t x, uint64_t y)
|
||||
{
|
||||
return (x / y) + (x % y > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/* rounds up x to the nearest multiple of y */
|
||||
inline uint64_t fec_round_up(uint64_t x, uint64_t y)
|
||||
{
|
||||
return fec_div_round_up(x, y) * y;
|
||||
}
|
||||
|
||||
/* returns a physical offset for a byte in an RS block */
|
||||
inline uint64_t fec_ecc_interleave(uint64_t offset, int rsn, uint64_t rounds)
|
||||
{
|
||||
return (offset / rsn) + (offset % rsn) * rounds * FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
/* returns the size of ecc data given a file size and the number of roots */
|
||||
inline uint64_t fec_ecc_get_size(uint64_t file_size, int roots)
|
||||
{
|
||||
return fec_div_round_up(fec_div_round_up(file_size, FEC_BLOCKSIZE),
|
||||
FEC_RSM - roots)
|
||||
* roots * FEC_BLOCKSIZE
|
||||
+ FEC_BLOCKSIZE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* ___FEC_ECC_H___ */
|
194
android/system/extras/libfec/include/fec/io.h
Normal file
194
android/system/extras/libfec/include/fec/io.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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.
|
||||
*/
|
||||
|
||||
#ifndef ___FEC_IO_H___
|
||||
#define ___FEC_IO_H___
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef SHA256_DIGEST_LENGTH
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#endif
|
||||
|
||||
#define FEC_BLOCKSIZE 4096
|
||||
#define FEC_DEFAULT_ROOTS 2
|
||||
|
||||
#define FEC_MAGIC 0xFECFECFE
|
||||
#define FEC_VERSION 0
|
||||
|
||||
/* disk format for the header */
|
||||
struct fec_header {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t size;
|
||||
uint32_t roots;
|
||||
uint32_t fec_size;
|
||||
uint64_t inp_size;
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fec_status {
|
||||
int flags;
|
||||
int mode;
|
||||
uint64_t errors;
|
||||
uint64_t data_size;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct fec_ecc_metadata {
|
||||
bool valid;
|
||||
uint32_t roots;
|
||||
uint64_t blocks;
|
||||
uint64_t rounds;
|
||||
uint64_t start;
|
||||
};
|
||||
|
||||
struct fec_verity_metadata {
|
||||
bool disabled;
|
||||
uint64_t data_size;
|
||||
uint8_t signature[ANDROID_PUBKEY_MODULUS_SIZE];
|
||||
uint8_t ecc_signature[ANDROID_PUBKEY_MODULUS_SIZE];
|
||||
const char *table;
|
||||
uint32_t table_length;
|
||||
};
|
||||
|
||||
/* flags for fec_open */
|
||||
enum {
|
||||
FEC_FS_EXT4 = 1 << 0,
|
||||
FEC_FS_SQUASH = 1 << 1,
|
||||
FEC_VERITY_DISABLE = 1 << 8
|
||||
};
|
||||
|
||||
struct fec_handle;
|
||||
|
||||
/* file access */
|
||||
extern int fec_open(struct fec_handle **f, const char *path, int mode,
|
||||
int flags, int roots);
|
||||
|
||||
extern int fec_close(struct fec_handle *f);
|
||||
|
||||
extern int fec_verity_set_status(struct fec_handle *f, bool enabled);
|
||||
|
||||
extern int fec_verity_get_metadata(struct fec_handle *f,
|
||||
struct fec_verity_metadata *data);
|
||||
|
||||
extern int fec_ecc_get_metadata(struct fec_handle *f,
|
||||
struct fec_ecc_metadata *data);
|
||||
|
||||
extern int fec_get_status(struct fec_handle *f, struct fec_status *s);
|
||||
|
||||
extern int fec_seek(struct fec_handle *f, int64_t offset, int whence);
|
||||
|
||||
extern ssize_t fec_read(struct fec_handle *f, void *buf, size_t count);
|
||||
|
||||
extern ssize_t fec_pread(struct fec_handle *f, void *buf, size_t count,
|
||||
uint64_t offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/* C++ wrappers for fec_handle and operations */
|
||||
namespace fec {
|
||||
using handle = std::unique_ptr<fec_handle, decltype(&fec_close)>;
|
||||
|
||||
class io {
|
||||
public:
|
||||
io() : handle_(nullptr, fec_close) {}
|
||||
|
||||
explicit io(const std::string& fn, int mode = O_RDONLY, int flags = 0,
|
||||
int roots = FEC_DEFAULT_ROOTS) : handle_(nullptr, fec_close) {
|
||||
open(fn, mode, flags, roots);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return !!handle_;
|
||||
}
|
||||
|
||||
bool open(const std::string& fn, int mode = O_RDONLY, int flags = 0,
|
||||
int roots = FEC_DEFAULT_ROOTS)
|
||||
{
|
||||
fec_handle *fh = nullptr;
|
||||
int rc = fec_open(&fh, fn.c_str(), mode, flags, roots);
|
||||
if (!rc) {
|
||||
handle_.reset(fh);
|
||||
}
|
||||
return !rc;
|
||||
}
|
||||
|
||||
bool close() {
|
||||
return !fec_close(handle_.release());
|
||||
}
|
||||
|
||||
bool seek(int64_t offset, int whence) {
|
||||
return !fec_seek(handle_.get(), offset, whence);
|
||||
}
|
||||
|
||||
ssize_t read(void *buf, size_t count) {
|
||||
return fec_read(handle_.get(), buf, count);
|
||||
}
|
||||
|
||||
ssize_t pread(void *buf, size_t count, uint64_t offset) {
|
||||
return fec_pread(handle_.get(), buf, count, offset);
|
||||
}
|
||||
|
||||
bool get_status(fec_status& status) {
|
||||
return !fec_get_status(handle_.get(), &status);
|
||||
}
|
||||
|
||||
bool get_verity_metadata(fec_verity_metadata& data) {
|
||||
return !fec_verity_get_metadata(handle_.get(), &data);
|
||||
}
|
||||
|
||||
bool has_verity() {
|
||||
fec_verity_metadata data;
|
||||
return get_verity_metadata(data);
|
||||
}
|
||||
|
||||
bool get_ecc_metadata(fec_ecc_metadata& data) {
|
||||
return !fec_ecc_get_metadata(handle_.get(), &data);
|
||||
}
|
||||
|
||||
bool has_ecc() {
|
||||
fec_ecc_metadata data;
|
||||
return get_ecc_metadata(data) && data.valid;
|
||||
}
|
||||
|
||||
bool set_verity_status(bool enabled) {
|
||||
return !fec_verity_set_status(handle_.get(), enabled);
|
||||
}
|
||||
|
||||
private:
|
||||
handle handle_;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ___FEC_IO_H___ */
|
33
android/system/extras/libfec/test/Android.mk
Normal file
33
android/system/extras/libfec/test/Android.mk
Normal file
|
@ -0,0 +1,33 @@
|
|||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SANITIZE := integer
|
||||
LOCAL_MODULE := fec_test_read
|
||||
LOCAL_SRC_FILES := test_read.cpp
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libfec \
|
||||
libfec_rs \
|
||||
libcrypto_utils \
|
||||
libcrypto \
|
||||
libext4_utils \
|
||||
libsquashfs_utils \
|
||||
libbase
|
||||
LOCAL_CFLAGS := -Wall -Werror -D_GNU_SOURCE
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SANITIZE := integer
|
||||
LOCAL_MODULE := fec_test_rs
|
||||
LOCAL_SRC_FILES := test_rs.c
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_STATIC_LIBRARIES := libfec_rs
|
||||
LOCAL_CFLAGS := -Wall -Werror -D_GNU_SOURCE
|
||||
LOCAL_C_INCLUDES += external/fec
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif # HOST_OS == linux
|
71
android/system/extras/libfec/test/test_read.cpp
Normal file
71
android/system/extras/libfec/test/test_read.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 <new>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <fec/io.h>
|
||||
|
||||
using namespace std;
|
||||
const unsigned bufsize = 2 * 1024 * FEC_BLOCKSIZE;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
cerr << "usage: " << argv[0] << " input output" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
unique_ptr<uint8_t[]> buffer(new (nothrow) uint8_t[bufsize]);
|
||||
|
||||
if (!buffer) {
|
||||
cerr << "failed to allocate buffer" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
fec::io input(argv[1]);
|
||||
|
||||
if (!input) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ofstream output(argv[2], ios::binary | ios::trunc);
|
||||
|
||||
if (!output) {
|
||||
cerr << "failed to open " << argv[2] << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t count;
|
||||
|
||||
do {
|
||||
count = input.read(buffer.get(), bufsize);
|
||||
|
||||
if (count == -1) {
|
||||
return 1;
|
||||
} else if (count > 0) {
|
||||
output.write(reinterpret_cast<const char *>(buffer.get()), count);
|
||||
|
||||
if (!output) {
|
||||
cerr << "write" << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (count > 0);
|
||||
|
||||
return 0;
|
||||
}
|
93
android/system/extras/libfec/test/test_rs.c
Normal file
93
android/system/extras/libfec/test/test_rs.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source 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 <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fec.h>
|
||||
|
||||
#define FEC_RSM 255
|
||||
#define FEC_ROOTS 16
|
||||
#define FEC_RSN (FEC_RSM - FEC_ROOTS)
|
||||
#define FEC_PARAMS(roots) \
|
||||
8, 0x11d, 0, 1, (roots), 0
|
||||
|
||||
int main()
|
||||
{
|
||||
uint8_t data[FEC_RSM];
|
||||
uint8_t dupl[FEC_RSM];
|
||||
uint8_t corr[FEC_RSM];
|
||||
int i, rc, neras, errors;
|
||||
int erasures[FEC_RSM];
|
||||
void *rs;
|
||||
|
||||
memset(data, 0x00, sizeof(data));
|
||||
memset(corr, 0x00, sizeof(corr));
|
||||
|
||||
rs = init_rs_char(FEC_PARAMS(FEC_ROOTS));
|
||||
|
||||
if (!rs) {
|
||||
perror("init_rs_char");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
encode_rs_char(rs, data, &corr[FEC_RSN]);
|
||||
|
||||
for (neras = 1; neras <= FEC_ROOTS; ++neras) {
|
||||
printf("%d errors\n", neras);
|
||||
|
||||
for (i = 0; i < neras; ++i) {
|
||||
corr[i] = 0xFD;
|
||||
erasures[i] = i;
|
||||
}
|
||||
|
||||
memcpy(dupl, corr, sizeof(corr));
|
||||
|
||||
rc = decode_rs_char(rs, corr, NULL, 0);
|
||||
|
||||
printf("\tno erasures: %d\n", rc);
|
||||
|
||||
errors = 0;
|
||||
for (i = 0; i < FEC_RSN; ++i) {
|
||||
if (corr[i] != 0x00) {
|
||||
printf("\t\terror at %d (%02x)\n", i, corr[i]);
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
printf("\t\t%d errors in output\n", errors);
|
||||
|
||||
rc = decode_rs_char(rs, dupl, erasures, neras);
|
||||
|
||||
printf("\terasures: %d\n", rc);
|
||||
|
||||
errors = 0;
|
||||
for (i = 0; i < FEC_RSN; ++i) {
|
||||
if (dupl[i] != 0x00) {
|
||||
printf("\t\terror at %d (%02x)\n", i, dupl[i]);
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
printf("\t\t%d errors in output\n", errors);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue