upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
23
android/system/extras/verity/fec/Android.mk
Normal file
23
android/system/extras/verity/fec/Android.mk
Normal file
|
@ -0,0 +1,23 @@
|
|||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_SANITIZE := integer
|
||||
endif
|
||||
LOCAL_MODULE := fec
|
||||
LOCAL_SRC_FILES := main.cpp image.cpp
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libsparse \
|
||||
libz \
|
||||
libcrypto_utils \
|
||||
libcrypto \
|
||||
libfec \
|
||||
libfec_rs \
|
||||
libext4_utils \
|
||||
libsquashfs_utils
|
||||
LOCAL_SHARED_LIBRARIES := libbase
|
||||
LOCAL_CFLAGS += -Wall -Werror -O3
|
||||
LOCAL_C_INCLUDES += external/fec
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
444
android/system/extras/verity/fec/image.cpp
Normal file
444
android/system/extras/verity/fec/image.cpp
Normal file
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#define _LARGEFILE64_SOURCE
|
||||
|
||||
extern "C" {
|
||||
#include <fec.h>
|
||||
}
|
||||
|
||||
#include <assert.h>
|
||||
#include <android-base/file.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sparse/sparse.h>
|
||||
#include "image.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/fs.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/disk.h>
|
||||
#define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
void image_init(image *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
void image_free(image *ctx)
|
||||
{
|
||||
assert(ctx->input == ctx->output);
|
||||
|
||||
if (ctx->input) {
|
||||
delete[] ctx->input;
|
||||
}
|
||||
|
||||
if (ctx->fec) {
|
||||
delete[] ctx->fec;
|
||||
}
|
||||
|
||||
image_init(ctx);
|
||||
}
|
||||
|
||||
static void calculate_rounds(uint64_t size, image *ctx)
|
||||
{
|
||||
if (!size) {
|
||||
FATAL("empty file?\n");
|
||||
} else if (size % FEC_BLOCKSIZE) {
|
||||
FATAL("file size %" PRIu64 " is not a multiple of %u bytes\n",
|
||||
size, FEC_BLOCKSIZE);
|
||||
}
|
||||
|
||||
ctx->inp_size = size;
|
||||
ctx->blocks = fec_div_round_up(ctx->inp_size, FEC_BLOCKSIZE);
|
||||
ctx->rounds = fec_div_round_up(ctx->blocks, ctx->rs_n);
|
||||
}
|
||||
|
||||
static int process_chunk(void *priv, const void *data, int len)
|
||||
{
|
||||
image *ctx = (image *)priv;
|
||||
assert(len % FEC_BLOCKSIZE == 0);
|
||||
|
||||
if (data) {
|
||||
memcpy(&ctx->input[ctx->pos], data, len);
|
||||
}
|
||||
|
||||
ctx->pos += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void file_image_load(const std::vector<int>& fds, image *ctx)
|
||||
{
|
||||
uint64_t size = 0;
|
||||
std::vector<struct sparse_file *> files;
|
||||
|
||||
for (auto fd : fds) {
|
||||
uint64_t len = 0;
|
||||
struct sparse_file *file;
|
||||
|
||||
if (ctx->sparse) {
|
||||
file = sparse_file_import(fd, false, false);
|
||||
} else {
|
||||
file = sparse_file_import_auto(fd, false, ctx->verbose);
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
FATAL("failed to read file %s\n", ctx->fec_filename);
|
||||
}
|
||||
|
||||
len = sparse_file_len(file, false, false);
|
||||
files.push_back(file);
|
||||
|
||||
size += len;
|
||||
}
|
||||
|
||||
calculate_rounds(size, ctx);
|
||||
|
||||
if (ctx->verbose) {
|
||||
INFO("allocating %" PRIu64 " bytes of memory\n", ctx->inp_size);
|
||||
}
|
||||
|
||||
ctx->input = new uint8_t[ctx->inp_size];
|
||||
|
||||
if (!ctx->input) {
|
||||
FATAL("failed to allocate memory\n");
|
||||
}
|
||||
|
||||
memset(ctx->input, 0, ctx->inp_size);
|
||||
ctx->output = ctx->input;
|
||||
ctx->pos = 0;
|
||||
|
||||
for (auto file : files) {
|
||||
sparse_file_callback(file, false, false, process_chunk, ctx);
|
||||
sparse_file_destroy(file);
|
||||
}
|
||||
|
||||
for (auto fd : fds) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
bool image_load(const std::vector<std::string>& filenames, image *ctx)
|
||||
{
|
||||
assert(ctx->roots > 0 && ctx->roots < FEC_RSM);
|
||||
ctx->rs_n = FEC_RSM - ctx->roots;
|
||||
|
||||
int flags = O_RDONLY;
|
||||
|
||||
if (ctx->inplace) {
|
||||
flags = O_RDWR;
|
||||
}
|
||||
|
||||
std::vector<int> fds;
|
||||
|
||||
for (const auto& fn : filenames) {
|
||||
int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), flags | O_LARGEFILE));
|
||||
|
||||
if (fd < 0) {
|
||||
FATAL("failed to open file '%s': %s\n", fn.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
fds.push_back(fd);
|
||||
}
|
||||
|
||||
file_image_load(fds, ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_save(const std::string& filename, image *ctx)
|
||||
{
|
||||
/* TODO: support saving as a sparse file */
|
||||
int fd = TEMP_FAILURE_RETRY(open(filename.c_str(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC, 0666));
|
||||
|
||||
if (fd < 0) {
|
||||
FATAL("failed to open file '%s: %s'\n", filename.c_str(),
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (!android::base::WriteFully(fd, ctx->output, ctx->inp_size)) {
|
||||
FATAL("failed to write to output: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_ecc_new(const std::string& filename, image *ctx)
|
||||
{
|
||||
assert(ctx->rounds > 0); /* image_load should be called first */
|
||||
|
||||
ctx->fec_filename = filename.c_str();
|
||||
ctx->fec_size = ctx->rounds * ctx->roots * FEC_BLOCKSIZE;
|
||||
|
||||
if (ctx->verbose) {
|
||||
INFO("allocating %u bytes of memory\n", ctx->fec_size);
|
||||
}
|
||||
|
||||
ctx->fec = new uint8_t[ctx->fec_size];
|
||||
|
||||
if (!ctx->fec) {
|
||||
FATAL("failed to allocate %u bytes\n", ctx->fec_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_ecc_load(const std::string& filename, image *ctx)
|
||||
{
|
||||
int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
|
||||
|
||||
if (fd < 0) {
|
||||
FATAL("failed to open file '%s': %s\n", filename.c_str(),
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (lseek64(fd, -FEC_BLOCKSIZE, SEEK_END) < 0) {
|
||||
FATAL("failed to seek to header in '%s': %s\n", filename.c_str(),
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
assert(sizeof(fec_header) <= FEC_BLOCKSIZE);
|
||||
|
||||
uint8_t header[FEC_BLOCKSIZE];
|
||||
fec_header *p = (fec_header *)header;
|
||||
|
||||
if (!android::base::ReadFully(fd, header, sizeof(header))) {
|
||||
FATAL("failed to read %zd bytes from '%s': %s\n", sizeof(header),
|
||||
filename.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
if (p->magic != FEC_MAGIC) {
|
||||
FATAL("invalid magic in '%s': %08x\n", filename.c_str(), p->magic);
|
||||
}
|
||||
|
||||
if (p->version != FEC_VERSION) {
|
||||
FATAL("unsupported version in '%s': %u\n", filename.c_str(),
|
||||
p->version);
|
||||
}
|
||||
|
||||
if (p->size != sizeof(fec_header)) {
|
||||
FATAL("unexpected header size in '%s': %u\n", filename.c_str(),
|
||||
p->size);
|
||||
}
|
||||
|
||||
if (p->roots == 0 || p->roots >= FEC_RSM) {
|
||||
FATAL("invalid roots in '%s': %u\n", filename.c_str(), p->roots);
|
||||
}
|
||||
|
||||
if (p->fec_size % p->roots || p->fec_size % FEC_BLOCKSIZE) {
|
||||
FATAL("invalid length in '%s': %u\n", filename.c_str(), p->fec_size);
|
||||
}
|
||||
|
||||
ctx->roots = (int)p->roots;
|
||||
ctx->rs_n = FEC_RSM - ctx->roots;
|
||||
|
||||
calculate_rounds(p->inp_size, ctx);
|
||||
|
||||
if (!image_ecc_new(filename, ctx)) {
|
||||
FATAL("failed to allocate ecc\n");
|
||||
}
|
||||
|
||||
if (p->fec_size != ctx->fec_size) {
|
||||
FATAL("inconsistent header in '%s'\n", filename.c_str());
|
||||
}
|
||||
|
||||
if (lseek64(fd, 0, SEEK_SET) < 0) {
|
||||
FATAL("failed to rewind '%s': %s", filename.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
if (!android::base::ReadFully(fd, ctx->fec, ctx->fec_size)) {
|
||||
FATAL("failed to read %u bytes from '%s': %s\n", ctx->fec_size,
|
||||
filename.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256(ctx->fec, ctx->fec_size, hash);
|
||||
|
||||
if (memcmp(hash, p->hash, SHA256_DIGEST_LENGTH) != 0) {
|
||||
FATAL("invalid ecc data\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_ecc_save(image *ctx)
|
||||
{
|
||||
assert(2 * sizeof(fec_header) <= FEC_BLOCKSIZE);
|
||||
|
||||
uint8_t header[FEC_BLOCKSIZE] = {0};
|
||||
|
||||
fec_header *f = (fec_header *)header;
|
||||
|
||||
f->magic = FEC_MAGIC;
|
||||
f->version = FEC_VERSION;
|
||||
f->size = sizeof(fec_header);
|
||||
f->roots = ctx->roots;
|
||||
f->fec_size = ctx->fec_size;
|
||||
f->inp_size = ctx->inp_size;
|
||||
|
||||
SHA256(ctx->fec, ctx->fec_size, f->hash);
|
||||
|
||||
/* store a copy of the fec_header at the end of the header block */
|
||||
memcpy(&header[sizeof(header) - sizeof(fec_header)], header,
|
||||
sizeof(fec_header));
|
||||
|
||||
assert(ctx->fec_filename);
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC, 0666));
|
||||
|
||||
if (fd < 0) {
|
||||
FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (!android::base::WriteFully(fd, ctx->fec, ctx->fec_size)) {
|
||||
FATAL("failed to write to output: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
if (ctx->padding > 0) {
|
||||
uint8_t padding[FEC_BLOCKSIZE] = {0};
|
||||
|
||||
for (uint32_t i = 0; i < ctx->padding; i += FEC_BLOCKSIZE) {
|
||||
if (!android::base::WriteFully(fd, padding, FEC_BLOCKSIZE)) {
|
||||
FATAL("failed to write padding: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!android::base::WriteFully(fd, header, sizeof(header))) {
|
||||
FATAL("failed to write to header: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void * process(void *cookie)
|
||||
{
|
||||
image_proc_ctx *ctx = (image_proc_ctx *)cookie;
|
||||
ctx->func(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool image_process(image_proc_func func, image *ctx)
|
||||
{
|
||||
int threads = ctx->threads;
|
||||
|
||||
if (threads < IMAGE_MIN_THREADS) {
|
||||
threads = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
if (threads < IMAGE_MIN_THREADS) {
|
||||
threads = IMAGE_MIN_THREADS;
|
||||
}
|
||||
}
|
||||
|
||||
assert(ctx->rounds > 0);
|
||||
|
||||
if ((uint64_t)threads > ctx->rounds) {
|
||||
threads = (int)ctx->rounds;
|
||||
}
|
||||
if (threads > IMAGE_MAX_THREADS) {
|
||||
threads = IMAGE_MAX_THREADS;
|
||||
}
|
||||
|
||||
if (ctx->verbose) {
|
||||
INFO("starting %d threads to compute RS(255, %d)\n", threads,
|
||||
ctx->rs_n);
|
||||
}
|
||||
|
||||
pthread_t pthreads[threads];
|
||||
image_proc_ctx args[threads];
|
||||
|
||||
uint64_t current = 0;
|
||||
uint64_t end = ctx->rounds * ctx->rs_n * FEC_BLOCKSIZE;
|
||||
uint64_t rs_blocks_per_thread =
|
||||
fec_div_round_up(ctx->rounds * FEC_BLOCKSIZE, threads);
|
||||
|
||||
if (ctx->verbose) {
|
||||
INFO("computing %" PRIu64 " codes per thread\n", rs_blocks_per_thread);
|
||||
}
|
||||
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
args[i].func = func;
|
||||
args[i].id = i;
|
||||
args[i].ctx = ctx;
|
||||
args[i].rv = 0;
|
||||
args[i].fec_pos = current * ctx->roots;
|
||||
args[i].start = current * ctx->rs_n;
|
||||
args[i].end = (current + rs_blocks_per_thread) * ctx->rs_n;
|
||||
|
||||
args[i].rs = init_rs_char(FEC_PARAMS(ctx->roots));
|
||||
|
||||
if (!args[i].rs) {
|
||||
FATAL("failed to initialize encoder for thread %d\n", i);
|
||||
}
|
||||
|
||||
if (args[i].end > end) {
|
||||
args[i].end = end;
|
||||
} else if (i == threads && args[i].end + rs_blocks_per_thread *
|
||||
ctx->rs_n > end) {
|
||||
args[i].end = end;
|
||||
}
|
||||
|
||||
if (ctx->verbose) {
|
||||
INFO("thread %d: [%" PRIu64 ", %" PRIu64 ")\n",
|
||||
i, args[i].start, args[i].end);
|
||||
}
|
||||
|
||||
assert(args[i].start < args[i].end);
|
||||
assert((args[i].end - args[i].start) % ctx->rs_n == 0);
|
||||
|
||||
if (pthread_create(&pthreads[i], NULL, process, &args[i]) != 0) {
|
||||
FATAL("failed to create thread %d\n", i);
|
||||
}
|
||||
|
||||
current += rs_blocks_per_thread;
|
||||
}
|
||||
|
||||
ctx->rv = 0;
|
||||
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
if (pthread_join(pthreads[i], NULL) != 0) {
|
||||
FATAL("failed to join thread %d: %s\n", i, strerror(errno));
|
||||
}
|
||||
|
||||
ctx->rv += args[i].rv;
|
||||
|
||||
if (args[i].rs) {
|
||||
free_rs_char(args[i].rs);
|
||||
args[i].rs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
115
android/system/extras/verity/fec/image.h
Normal file
115
android/system/extras/verity/fec/image.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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_H__
|
||||
#define __FEC_H__
|
||||
|
||||
#include <utils/Compat.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fec/io.h>
|
||||
#include <fec/ecc.h>
|
||||
|
||||
#define IMAGE_MIN_THREADS 1
|
||||
#define IMAGE_MAX_THREADS 128
|
||||
|
||||
#define INFO(x...) \
|
||||
fprintf(stderr, x);
|
||||
#define FATAL(x...) { \
|
||||
fprintf(stderr, x); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
struct image {
|
||||
/* if true, decode file in place instead of creating a new output file */
|
||||
bool inplace;
|
||||
/* if true, assume input is a sparse file */
|
||||
bool sparse;
|
||||
/* if true, print more verbose information to stderr */
|
||||
bool verbose;
|
||||
const char *fec_filename;
|
||||
int fec_fd;
|
||||
int inp_fd;
|
||||
/* the number of Reed-Solomon generator polynomial roots, also the number
|
||||
of parity bytes generated for each N bytes in RS(M, N) */
|
||||
int roots;
|
||||
/* for RS(M, N), N = M - roots */
|
||||
int rs_n;
|
||||
int threads;
|
||||
uint32_t fec_size;
|
||||
uint32_t padding;
|
||||
uint64_t blocks;
|
||||
uint64_t inp_size;
|
||||
uint64_t pos;
|
||||
uint64_t rounds;
|
||||
uint64_t rv;
|
||||
uint8_t *fec;
|
||||
uint8_t *input;
|
||||
uint8_t *output;
|
||||
};
|
||||
|
||||
struct image_proc_ctx;
|
||||
typedef void (*image_proc_func)(image_proc_ctx *);
|
||||
|
||||
struct image_proc_ctx {
|
||||
image_proc_func func;
|
||||
int id;
|
||||
image *ctx;
|
||||
uint64_t rv;
|
||||
uint64_t fec_pos;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
void *rs;
|
||||
};
|
||||
|
||||
extern bool image_load(const std::vector<std::string>& filename, image *ctx);
|
||||
extern bool image_save(const std::string& filename, image *ctx);
|
||||
|
||||
extern bool image_ecc_new(const std::string& filename, image *ctx);
|
||||
extern bool image_ecc_load(const std::string& filename, image *ctx);
|
||||
extern bool image_ecc_save(image *ctx);
|
||||
|
||||
extern bool image_process(image_proc_func f, image *ctx);
|
||||
|
||||
extern void image_init(image *ctx);
|
||||
extern void image_free(image *ctx);
|
||||
|
||||
inline uint8_t image_get_interleaved_byte(uint64_t i, image *ctx)
|
||||
{
|
||||
uint64_t offset = fec_ecc_interleave(i, ctx->rs_n, ctx->rounds);
|
||||
|
||||
if (unlikely(offset >= ctx->inp_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctx->input[offset];
|
||||
}
|
||||
|
||||
inline void image_set_interleaved_byte(uint64_t i, image *ctx,
|
||||
uint8_t value)
|
||||
{
|
||||
uint64_t offset = fec_ecc_interleave(i, ctx->rs_n, ctx->rounds);
|
||||
|
||||
if (unlikely(offset >= ctx->inp_size)) {
|
||||
assert(value == 0);
|
||||
} else if (ctx->output && ctx->output[offset] != value) {
|
||||
ctx->output[offset] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __FEC_H__
|
412
android/system/extras/verity/fec/main.cpp
Normal file
412
android/system/extras/verity/fec/main.cpp
Normal file
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <fec.h>
|
||||
}
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include "image.h"
|
||||
|
||||
enum {
|
||||
MODE_ENCODE,
|
||||
MODE_DECODE,
|
||||
MODE_PRINTSIZE,
|
||||
MODE_GETECCSTART,
|
||||
MODE_GETVERITYSTART
|
||||
};
|
||||
|
||||
static void encode_rs(struct image_proc_ctx *ctx)
|
||||
{
|
||||
struct image *fcx = ctx->ctx;
|
||||
int j;
|
||||
uint8_t data[fcx->rs_n];
|
||||
uint64_t i;
|
||||
|
||||
for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
|
||||
for (j = 0; j < fcx->rs_n; ++j) {
|
||||
data[j] = image_get_interleaved_byte(i + j, fcx);
|
||||
}
|
||||
|
||||
encode_rs_char(ctx->rs, data, &fcx->fec[ctx->fec_pos]);
|
||||
ctx->fec_pos += fcx->roots;
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_rs(struct image_proc_ctx *ctx)
|
||||
{
|
||||
struct image *fcx = ctx->ctx;
|
||||
int j, rv;
|
||||
uint8_t data[fcx->rs_n + fcx->roots];
|
||||
uint64_t i;
|
||||
|
||||
assert(sizeof(data) == FEC_RSM);
|
||||
|
||||
for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
|
||||
for (j = 0; j < fcx->rs_n; ++j) {
|
||||
data[j] = image_get_interleaved_byte(i + j, fcx);
|
||||
}
|
||||
|
||||
memcpy(&data[fcx->rs_n], &fcx->fec[ctx->fec_pos], fcx->roots);
|
||||
rv = decode_rs_char(ctx->rs, data, NULL, 0);
|
||||
|
||||
if (rv < 0) {
|
||||
FATAL("failed to recover [%" PRIu64 ", %" PRIu64 ")\n",
|
||||
i, i + fcx->rs_n);
|
||||
} else if (rv > 0) {
|
||||
/* copy corrected data to output */
|
||||
for (j = 0; j < fcx->rs_n; ++j) {
|
||||
image_set_interleaved_byte(i + j, fcx, data[j]);
|
||||
}
|
||||
|
||||
ctx->rv += rv;
|
||||
}
|
||||
|
||||
ctx->fec_pos += fcx->roots;
|
||||
}
|
||||
}
|
||||
|
||||
static int usage()
|
||||
{
|
||||
printf("fec: a tool for encoding and decoding files using RS(255, N).\n"
|
||||
"\n"
|
||||
"usage: fec <mode> [ <options> ] [ <data> <fec> [ <output> ] ]\n"
|
||||
"mode:\n"
|
||||
" -e --encode encode (default)\n"
|
||||
" -d --decode decode\n"
|
||||
" -s, --print-fec-size=<data size> print FEC size\n"
|
||||
" -E, --get-ecc-start=data print ECC offset in data\n"
|
||||
" -V, --get-verity-start=data print verity offset\n"
|
||||
"options:\n"
|
||||
" -h show this help\n"
|
||||
" -v enable verbose logging\n"
|
||||
" -r, --roots=<bytes> number of parity bytes\n"
|
||||
" -j, --threads=<threads> number of threads to use\n"
|
||||
" -S treat data as a sparse file\n"
|
||||
"encoding options:\n"
|
||||
" -p, --padding=<bytes> add padding after ECC data\n"
|
||||
"decoding options:\n"
|
||||
" -i, --inplace correct <data> in place\n"
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint64_t parse_arg(const char *arg, const char *name, uint64_t maxval)
|
||||
{
|
||||
char* endptr;
|
||||
errno = 0;
|
||||
|
||||
unsigned long long int value = strtoull(arg, &endptr, 0);
|
||||
|
||||
if (arg[0] == '\0' || *endptr != '\0' ||
|
||||
(errno == ERANGE && value == ULLONG_MAX)) {
|
||||
FATAL("invalid value of %s\n", name);
|
||||
}
|
||||
if (value > maxval) {
|
||||
FATAL("value of roots too large (max. %" PRIu64 ")\n", maxval);
|
||||
}
|
||||
|
||||
return (uint64_t)value;
|
||||
}
|
||||
|
||||
static int print_size(image& ctx)
|
||||
{
|
||||
/* output size including header */
|
||||
printf("%" PRIu64 "\n", fec_ecc_get_size(ctx.inp_size, ctx.roots));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_start(int mode, const std::string& filename)
|
||||
{
|
||||
fec::io fh(filename, O_RDONLY, FEC_VERITY_DISABLE);
|
||||
|
||||
if (!fh) {
|
||||
FATAL("failed to open input\n");
|
||||
}
|
||||
|
||||
if (mode == MODE_GETECCSTART) {
|
||||
fec_ecc_metadata data;
|
||||
|
||||
if (!fh.get_ecc_metadata(data)) {
|
||||
FATAL("no ecc data\n");
|
||||
}
|
||||
|
||||
printf("%" PRIu64 "\n", data.start);
|
||||
} else {
|
||||
fec_verity_metadata data;
|
||||
|
||||
if (!fh.get_verity_metadata(data)) {
|
||||
FATAL("no verity data\n");
|
||||
}
|
||||
|
||||
printf("%" PRIu64 "\n", data.data_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encode(image& ctx, const std::vector<std::string>& inp_filenames,
|
||||
const std::string& fec_filename)
|
||||
{
|
||||
if (ctx.inplace) {
|
||||
FATAL("invalid parameters: inplace can only used when decoding\n");
|
||||
}
|
||||
|
||||
if (!image_load(inp_filenames, &ctx)) {
|
||||
FATAL("failed to read input\n");
|
||||
}
|
||||
|
||||
if (!image_ecc_new(fec_filename, &ctx)) {
|
||||
FATAL("failed to allocate ecc\n");
|
||||
}
|
||||
|
||||
INFO("encoding RS(255, %d) to '%s' for input files:\n", ctx.rs_n,
|
||||
fec_filename.c_str());
|
||||
|
||||
size_t n = 1;
|
||||
|
||||
for (const auto& fn : inp_filenames) {
|
||||
INFO("\t%zu: '%s'\n", n++, fn.c_str());
|
||||
}
|
||||
|
||||
if (ctx.verbose) {
|
||||
INFO("\traw fec size: %u\n", ctx.fec_size);
|
||||
INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
|
||||
INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
|
||||
}
|
||||
|
||||
if (!image_process(encode_rs, &ctx)) {
|
||||
FATAL("failed to process input\n");
|
||||
}
|
||||
|
||||
if (!image_ecc_save(&ctx)) {
|
||||
FATAL("failed to write output\n");
|
||||
}
|
||||
|
||||
image_free(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode(image& ctx, const std::vector<std::string>& inp_filenames,
|
||||
const std::string& fec_filename, std::string& out_filename)
|
||||
{
|
||||
const std::string& inp_filename = inp_filenames.front();
|
||||
|
||||
if (ctx.inplace && ctx.sparse) {
|
||||
FATAL("invalid parameters: inplace cannot be used with sparse "
|
||||
"files\n");
|
||||
}
|
||||
|
||||
if (ctx.padding) {
|
||||
FATAL("invalid parameters: padding is only relevant when encoding\n");
|
||||
}
|
||||
|
||||
if (!image_ecc_load(fec_filename, &ctx) ||
|
||||
!image_load(inp_filenames, &ctx)) {
|
||||
FATAL("failed to read input\n");
|
||||
}
|
||||
|
||||
if (ctx.inplace) {
|
||||
INFO("correcting '%s' using RS(255, %d) from '%s'\n",
|
||||
inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
|
||||
|
||||
out_filename = inp_filename;
|
||||
} else {
|
||||
INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
|
||||
inp_filename.c_str(),
|
||||
out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
|
||||
fec_filename.c_str());
|
||||
}
|
||||
|
||||
if (ctx.verbose) {
|
||||
INFO("\traw fec size: %u\n", ctx.fec_size);
|
||||
INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
|
||||
INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
|
||||
}
|
||||
|
||||
if (!image_process(decode_rs, &ctx)) {
|
||||
FATAL("failed to process input\n");
|
||||
}
|
||||
|
||||
if (ctx.rv) {
|
||||
INFO("corrected %" PRIu64 " errors\n", ctx.rv);
|
||||
} else {
|
||||
INFO("no errors found\n");
|
||||
}
|
||||
|
||||
if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
|
||||
FATAL("failed to write output\n");
|
||||
}
|
||||
|
||||
image_free(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::string fec_filename;
|
||||
std::string out_filename;
|
||||
std::vector<std::string> inp_filenames;
|
||||
int mode = MODE_ENCODE;
|
||||
image ctx;
|
||||
|
||||
image_init(&ctx);
|
||||
ctx.roots = FEC_DEFAULT_ROOTS;
|
||||
|
||||
while (1) {
|
||||
const static struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"encode", no_argument, 0, 'e'},
|
||||
{"decode", no_argument, 0, 'd'},
|
||||
{"sparse", no_argument, 0, 'S'},
|
||||
{"roots", required_argument, 0, 'r'},
|
||||
{"inplace", no_argument, 0, 'i'},
|
||||
{"threads", required_argument, 0, 'j'},
|
||||
{"print-fec-size", required_argument, 0, 's'},
|
||||
{"get-ecc-start", required_argument, 0, 'E'},
|
||||
{"get-verity-start", required_argument, 0, 'V'},
|
||||
{"padding", required_argument, 0, 'p'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, NULL);
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return usage();
|
||||
case 'S':
|
||||
ctx.sparse = true;
|
||||
break;
|
||||
case 'e':
|
||||
if (mode != MODE_ENCODE) {
|
||||
return usage();
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (mode != MODE_ENCODE) {
|
||||
return usage();
|
||||
}
|
||||
mode = MODE_DECODE;
|
||||
break;
|
||||
case 'r':
|
||||
ctx.roots = (int)parse_arg(optarg, "roots", FEC_RSM);
|
||||
break;
|
||||
case 'i':
|
||||
ctx.inplace = true;
|
||||
break;
|
||||
case 'j':
|
||||
ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
|
||||
break;
|
||||
case 's':
|
||||
if (mode != MODE_ENCODE) {
|
||||
return usage();
|
||||
}
|
||||
mode = MODE_PRINTSIZE;
|
||||
ctx.inp_size = parse_arg(optarg, "print-fec-size", UINT64_MAX);
|
||||
break;
|
||||
case 'E':
|
||||
if (mode != MODE_ENCODE) {
|
||||
return usage();
|
||||
}
|
||||
mode = MODE_GETECCSTART;
|
||||
inp_filenames.push_back(optarg);
|
||||
break;
|
||||
case 'V':
|
||||
if (mode != MODE_ENCODE) {
|
||||
return usage();
|
||||
}
|
||||
mode = MODE_GETVERITYSTART;
|
||||
inp_filenames.push_back(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
ctx.padding = (uint32_t)parse_arg(optarg, "padding", UINT32_MAX);
|
||||
if (ctx.padding % FEC_BLOCKSIZE) {
|
||||
FATAL("padding must be multiple of %u\n", FEC_BLOCKSIZE);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
ctx.verbose = true;
|
||||
break;
|
||||
case '?':
|
||||
return usage();
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
assert(ctx.roots > 0 && ctx.roots < FEC_RSM);
|
||||
|
||||
/* check for input / output parameters */
|
||||
if (mode == MODE_ENCODE) {
|
||||
/* allow multiple input files */
|
||||
for (int i = 0; i < (argc - 1); ++i) {
|
||||
inp_filenames.push_back(argv[i]);
|
||||
}
|
||||
|
||||
if (inp_filenames.empty()) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
/* the last one is the output file */
|
||||
fec_filename = argv[argc - 1];
|
||||
} else if (mode == MODE_DECODE) {
|
||||
if (argc < 2 || argc > 3) {
|
||||
return usage();
|
||||
} else if (argc == 3) {
|
||||
if (ctx.inplace) {
|
||||
return usage();
|
||||
}
|
||||
out_filename = argv[2];
|
||||
}
|
||||
|
||||
inp_filenames.push_back(argv[0]);
|
||||
fec_filename = argv[1];
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case MODE_PRINTSIZE:
|
||||
return print_size(ctx);
|
||||
case MODE_GETECCSTART:
|
||||
case MODE_GETVERITYSTART:
|
||||
return get_start(mode, inp_filenames.front());
|
||||
case MODE_ENCODE:
|
||||
return encode(ctx, inp_filenames, fec_filename);
|
||||
case MODE_DECODE:
|
||||
return decode(ctx, inp_filenames, fec_filename, out_filename);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
89
android/system/extras/verity/fec/tests/fec.py
Normal file
89
android/system/extras/verity/fec/tests/fec.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
# 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.
|
||||
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
blocksize = 4096
|
||||
roots = 2
|
||||
|
||||
def corrupt(image, offset, length):
|
||||
print "corrupting %d bytes at offset %d" % (length, offset)
|
||||
f = os.open(image, os.O_WRONLY)
|
||||
os.lseek(f, offset, os.SEEK_SET)
|
||||
os.write(f, os.urandom(length))
|
||||
os.close(f)
|
||||
|
||||
def corruptmax(image, roots):
|
||||
size = os.stat(image).st_size
|
||||
|
||||
blocks = int(math.ceil(float(size) / blocksize))
|
||||
rounds = int(math.ceil(float(blocks) / (255 - roots)))
|
||||
|
||||
max_errors = int(math.floor(rounds * roots / 2)) * blocksize
|
||||
offset = random.randrange(0, size - max_errors)
|
||||
|
||||
corrupt(image, offset, max_errors)
|
||||
|
||||
def encode(image, fec, roots):
|
||||
if subprocess.call([ "fec", "--roots= " + str(roots), image, fec ]) != 0:
|
||||
raise Exception("encoding failed")
|
||||
|
||||
def decode(image, fec, output):
|
||||
return subprocess.call([ "fec", "--decode", image, fec, output ])
|
||||
|
||||
def compare(a, b):
|
||||
return subprocess.call([ "cmp", "-s", a, b ])
|
||||
|
||||
def simg2img(image, output):
|
||||
print "creating a non-sparse copy of '%s' to '%s'" % (image, output)
|
||||
if subprocess.call([ "simg2img", image, output]) != 0:
|
||||
raise Exception("simg2img failed")
|
||||
|
||||
def main(argv):
|
||||
image = argv[0]
|
||||
|
||||
temp_img = tempfile.NamedTemporaryFile()
|
||||
temp_cor = tempfile.NamedTemporaryFile()
|
||||
temp_fec = tempfile.NamedTemporaryFile()
|
||||
temp_out = tempfile.NamedTemporaryFile()
|
||||
|
||||
simg2img(image, temp_img.name)
|
||||
simg2img(image, temp_cor.name)
|
||||
|
||||
encode(image, temp_fec.name, roots)
|
||||
corruptmax(temp_cor.name, roots)
|
||||
|
||||
if decode(temp_cor.name, temp_fec.name, temp_out.name) != 0:
|
||||
raise Exception("FAILED: failed to correct maximum expected errors")
|
||||
|
||||
if compare(temp_img.name, temp_out.name) != 0:
|
||||
raise Exception("FAILED: corrected file not identical")
|
||||
else:
|
||||
print "corrected content matches original"
|
||||
|
||||
corrupt(temp_cor.name, 0, blocksize)
|
||||
|
||||
if decode(temp_cor.name, temp_fec.name, temp_out.name) == 0:
|
||||
raise Exception("FAILED: corrected more than maximum number of errors?")
|
||||
|
||||
print "PASSED"
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
Loading…
Add table
Add a link
Reference in a new issue