129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen/*
229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Copyright (C) 2015 The Android Open Source Project
329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Licensed under the Apache License, Version 2.0 (the "License");
529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * you may not use this file except in compliance with the License.
629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * You may obtain a copy of the License at
729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *      http://www.apache.org/licenses/LICENSE-2.0
929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
1029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Unless required by applicable law or agreed to in writing, software
1129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * distributed under the License is distributed on an "AS IS" BASIS,
1229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * See the License for the specific language governing permissions and
1429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * limitations under the License.
1529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen */
1629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#undef NDEBUG
1829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#define _LARGEFILE64_SOURCE
1929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
2029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenextern "C" {
2129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    #include <fec.h>
2229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
2329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
2429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <assert.h>
2566dd09e8e2407082ce93bf0784de641298131912Elliott Hughes#include <android-base/file.h>
2629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <errno.h>
2729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <fcntl.h>
2829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <getopt.h>
2929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <openssl/sha.h>
3029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <pthread.h>
3129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <stdlib.h>
3229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <string.h>
3329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <sys/ioctl.h>
3429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <sys/mman.h>
3529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <sparse/sparse.h>
3629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include "image.h"
3729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
380403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen#if defined(__linux__)
390403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen    #include <linux/fs.h>
400403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen#elif defined(__APPLE__)
410403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen    #include <sys/disk.h>
420403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen    #define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
430403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen    #define O_LARGEFILE 0
440403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen#endif
450403024cd6763fff3a0118b15e68c0f7fe4766d5Sami Tolvanen
4629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenvoid image_init(image *ctx)
4729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
4829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    memset(ctx, 0, sizeof(*ctx));
4929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
5029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
518bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanenvoid image_free(image *ctx)
5229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
5329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(ctx->input == ctx->output);
5429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
5529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->input) {
5629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        delete[] ctx->input;
5729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
5829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
5929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->fec) {
6029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        delete[] ctx->fec;
6129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
6229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
6329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_init(ctx);
6429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
6529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
6629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic void calculate_rounds(uint64_t size, image *ctx)
6729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
6829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!size) {
6929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("empty file?\n");
7029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    } else if (size % FEC_BLOCKSIZE) {
7129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("file size %" PRIu64 " is not a multiple of %u bytes\n",
7229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            size, FEC_BLOCKSIZE);
7329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
7429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
7529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->inp_size = size;
7629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->blocks = fec_div_round_up(ctx->inp_size, FEC_BLOCKSIZE);
7729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->rounds = fec_div_round_up(ctx->blocks, ctx->rs_n);
7829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
7929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
8029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic int process_chunk(void *priv, const void *data, int len)
8129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
8229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image *ctx = (image *)priv;
8329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(len % FEC_BLOCKSIZE == 0);
8429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
8529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (data) {
8629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        memcpy(&ctx->input[ctx->pos], data, len);
8729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
8829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
8929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->pos += len;
9029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 0;
9129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
9229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
93f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenstatic void file_image_load(const std::vector<int>& fds, image *ctx)
9429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
95f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    uint64_t size = 0;
96f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    std::vector<struct sparse_file *> files;
97f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
98f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    for (auto fd : fds) {
99f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        uint64_t len = 0;
100f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        struct sparse_file *file;
10129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
102f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        if (ctx->sparse) {
103f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            file = sparse_file_import(fd, false, false);
104f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        } else {
105f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            file = sparse_file_import_auto(fd, false, ctx->verbose);
106f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        }
10729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
108f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        if (!file) {
109f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            FATAL("failed to read file %s\n", ctx->fec_filename);
110f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        }
11129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
112f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        len = sparse_file_len(file, false, false);
113f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        files.push_back(file);
11429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
115f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        size += len;
116f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    }
117f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
118f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    calculate_rounds(size, ctx);
11929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
12029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->verbose) {
12129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("allocating %" PRIu64 " bytes of memory\n", ctx->inp_size);
12229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
12329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
12429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->input = new uint8_t[ctx->inp_size];
12529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
12629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!ctx->input) {
12729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to allocate memory\n");
12829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
12929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
13029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    memset(ctx->input, 0, ctx->inp_size);
13129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->output = ctx->input;
132f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    ctx->pos = 0;
13329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
134f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    for (auto file : files) {
135f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        sparse_file_callback(file, false, false, process_chunk, ctx);
136f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        sparse_file_destroy(file);
137f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    }
13829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
139f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    for (auto fd : fds) {
140f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        close(fd);
141f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    }
14229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
14329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1448bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanenbool image_load(const std::vector<std::string>& filenames, image *ctx)
14529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
14629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(ctx->roots > 0 && ctx->roots < FEC_RSM);
14729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->rs_n = FEC_RSM - ctx->roots;
14829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
14929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    int flags = O_RDONLY;
15029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
15129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->inplace) {
15229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        flags = O_RDWR;
15329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
15429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
155f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    std::vector<int> fds;
15629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
157a3b37f4d08e757ce90a7b02ca0e15951455c477cChih-Hung Hsieh    for (const auto& fn : filenames) {
158f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), flags | O_LARGEFILE));
159f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
160f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        if (fd < 0) {
161f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            FATAL("failed to open file '%s': %s\n", fn.c_str(), strerror(errno));
162f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        }
163f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
164f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        fds.push_back(fd);
16529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
16629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1678bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    file_image_load(fds, ctx);
16829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
16929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
17029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
17129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
172f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenbool image_save(const std::string& filename, image *ctx)
17329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
17429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    /* TODO: support saving as a sparse file */
175f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    int fd = TEMP_FAILURE_RETRY(open(filename.c_str(),
176f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen                O_WRONLY | O_CREAT | O_TRUNC, 0666));
17729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
17829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (fd < 0) {
179f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("failed to open file '%s: %s'\n", filename.c_str(),
180f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            strerror(errno));
18129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
18229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
18329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!android::base::WriteFully(fd, ctx->output, ctx->inp_size)) {
18429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to write to output: %s\n", strerror(errno));
18529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
18629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
187f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    close(fd);
18829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
18929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
19029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1918bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanenbool image_ecc_new(const std::string& filename, image *ctx)
19229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
1938bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    assert(ctx->rounds > 0); /* image_load should be called first */
19429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1958bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    ctx->fec_filename = filename.c_str();
1968bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    ctx->fec_size = ctx->rounds * ctx->roots * FEC_BLOCKSIZE;
19729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
19829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->verbose) {
19929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("allocating %u bytes of memory\n", ctx->fec_size);
20029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
20129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
20229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->fec = new uint8_t[ctx->fec_size];
20329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
20429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!ctx->fec) {
20529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to allocate %u bytes\n", ctx->fec_size);
20629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
20729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
20829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
20929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
21029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
211f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenbool image_ecc_load(const std::string& filename, image *ctx)
21229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
213f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
21429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
21529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (fd < 0) {
216f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("failed to open file '%s': %s\n", filename.c_str(),
217f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            strerror(errno));
21829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
21929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
22029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (lseek64(fd, -FEC_BLOCKSIZE, SEEK_END) < 0) {
221f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("failed to seek to header in '%s': %s\n", filename.c_str(),
22229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            strerror(errno));
22329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
22429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
22529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(sizeof(fec_header) <= FEC_BLOCKSIZE);
22629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
22729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint8_t header[FEC_BLOCKSIZE];
22829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    fec_header *p = (fec_header *)header;
22929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
23029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!android::base::ReadFully(fd, header, sizeof(header))) {
23129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to read %zd bytes from '%s': %s\n", sizeof(header),
232f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            filename.c_str(), strerror(errno));
23329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
23429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
23529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->magic != FEC_MAGIC) {
236f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("invalid magic in '%s': %08x\n", filename.c_str(), p->magic);
23729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
23829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
23929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->version != FEC_VERSION) {
240f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("unsupported version in '%s': %u\n", filename.c_str(),
241f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            p->version);
24229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
24329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
24429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->size != sizeof(fec_header)) {
245f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("unexpected header size in '%s': %u\n", filename.c_str(),
246f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            p->size);
24729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
24829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
24929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->roots == 0 || p->roots >= FEC_RSM) {
250f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("invalid roots in '%s': %u\n", filename.c_str(), p->roots);
25129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
25229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
25329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->fec_size % p->roots || p->fec_size % FEC_BLOCKSIZE) {
254f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("invalid length in '%s': %u\n", filename.c_str(), p->fec_size);
25529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
25629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
25729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->roots = (int)p->roots;
25829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->rs_n = FEC_RSM - ctx->roots;
25929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
26029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    calculate_rounds(p->inp_size, ctx);
26129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
26229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_ecc_new(filename, ctx)) {
26329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to allocate ecc\n");
26429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
26529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
26629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (p->fec_size != ctx->fec_size) {
267f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("inconsistent header in '%s'\n", filename.c_str());
26829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
26929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
27029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (lseek64(fd, 0, SEEK_SET) < 0) {
271f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        FATAL("failed to rewind '%s': %s", filename.c_str(), strerror(errno));
27229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
27329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
2748bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    if (!android::base::ReadFully(fd, ctx->fec, ctx->fec_size)) {
27529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to read %u bytes from '%s': %s\n", ctx->fec_size,
276f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            filename.c_str(), strerror(errno));
27729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
27829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
279f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    close(fd);
28029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
28129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint8_t hash[SHA256_DIGEST_LENGTH];
28229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    SHA256(ctx->fec, ctx->fec_size, hash);
28329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
28429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (memcmp(hash, p->hash, SHA256_DIGEST_LENGTH) != 0) {
28529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("invalid ecc data\n");
28629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
28729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
28829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
28929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
29029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
29129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenbool image_ecc_save(image *ctx)
29229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
29385e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    assert(2 * sizeof(fec_header) <= FEC_BLOCKSIZE);
29429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
29585e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    uint8_t header[FEC_BLOCKSIZE] = {0};
29629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
29785e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    fec_header *f = (fec_header *)header;
29829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
29929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->magic = FEC_MAGIC;
30029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->version = FEC_VERSION;
30129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->size = sizeof(fec_header);
30229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->roots = ctx->roots;
30329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->fec_size = ctx->fec_size;
30429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    f->inp_size = ctx->inp_size;
30529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
30629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    SHA256(ctx->fec, ctx->fec_size, f->hash);
30729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
30829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    /* store a copy of the fec_header at the end of the header block */
30985e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    memcpy(&header[sizeof(header) - sizeof(fec_header)], header,
31085e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen        sizeof(fec_header));
31129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
3128bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    assert(ctx->fec_filename);
31329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
3148bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
3158bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen                O_WRONLY | O_CREAT | O_TRUNC, 0666));
31629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
3178bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    if (fd < 0) {
3188bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen        FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
3198bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen            strerror(errno));
3208bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    }
32129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
32285e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    if (!android::base::WriteFully(fd, ctx->fec, ctx->fec_size)) {
3238bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen        FATAL("failed to write to output: %s\n", strerror(errno));
32429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
32529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
32685e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    if (ctx->padding > 0) {
32785e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen        uint8_t padding[FEC_BLOCKSIZE] = {0};
32885e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen
32985e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen        for (uint32_t i = 0; i < ctx->padding; i += FEC_BLOCKSIZE) {
33085e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen            if (!android::base::WriteFully(fd, padding, FEC_BLOCKSIZE)) {
33185e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen                FATAL("failed to write padding: %s\n", strerror(errno));
33285e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen            }
33385e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen        }
33485e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    }
33585e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen
33685e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    if (!android::base::WriteFully(fd, header, sizeof(header))) {
33785e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen        FATAL("failed to write to header: %s\n", strerror(errno));
33885e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen    }
33985e592cb86c9cbca82852240f0148adbee1b74e8Sami Tolvanen
3408bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen    close(fd);
3418bad827700bae35005872f3e6d674c5144fda8ffSami Tolvanen
34229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
34329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
34429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
34529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic void * process(void *cookie)
34629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
34729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_proc_ctx *ctx = (image_proc_ctx *)cookie;
34829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->func(ctx);
34929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return NULL;
35029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
35129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
35229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenbool image_process(image_proc_func func, image *ctx)
35329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
35429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    int threads = ctx->threads;
35529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
35629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (threads < IMAGE_MIN_THREADS) {
35729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        threads = sysconf(_SC_NPROCESSORS_ONLN);
35829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
35929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (threads < IMAGE_MIN_THREADS) {
36029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            threads = IMAGE_MIN_THREADS;
36129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
36229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
36329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
36429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(ctx->rounds > 0);
36529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
36629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if ((uint64_t)threads > ctx->rounds) {
36729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        threads = (int)ctx->rounds;
36829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
36929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (threads > IMAGE_MAX_THREADS) {
37029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        threads = IMAGE_MAX_THREADS;
37129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
37229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
37329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->verbose) {
37429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("starting %d threads to compute RS(255, %d)\n", threads,
37529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx->rs_n);
37629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
37729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
37829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    pthread_t pthreads[threads];
37929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_proc_ctx args[threads];
38029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
38129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint64_t current = 0;
38229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint64_t end = ctx->rounds * ctx->rs_n * FEC_BLOCKSIZE;
38329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint64_t rs_blocks_per_thread =
38429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        fec_div_round_up(ctx->rounds * FEC_BLOCKSIZE, threads);
38529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
38629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx->verbose) {
38729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("computing %" PRIu64 " codes per thread\n", rs_blocks_per_thread);
38829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
38929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
39029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    for (int i = 0; i < threads; ++i) {
39129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].func = func;
39229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].id = i;
39329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].ctx = ctx;
39429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].rv = 0;
39529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].fec_pos = current * ctx->roots;
39629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].start = current * ctx->rs_n;
39729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].end = (current + rs_blocks_per_thread) * ctx->rs_n;
39829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
39929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        args[i].rs = init_rs_char(FEC_PARAMS(ctx->roots));
40029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
40129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (!args[i].rs) {
40229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("failed to initialize encoder for thread %d\n", i);
40329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
40429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
40529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (args[i].end > end) {
40629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            args[i].end = end;
40729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        } else if (i == threads && args[i].end + rs_blocks_per_thread *
40829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                                        ctx->rs_n > end) {
40929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            args[i].end = end;
41029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
41129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
41229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (ctx->verbose) {
41329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            INFO("thread %d: [%" PRIu64 ", %" PRIu64 ")\n",
41429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                i, args[i].start, args[i].end);
41529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
41629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
41729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        assert(args[i].start < args[i].end);
41829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        assert((args[i].end - args[i].start) % ctx->rs_n == 0);
41929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
42029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (pthread_create(&pthreads[i], NULL, process, &args[i]) != 0) {
42129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("failed to create thread %d\n", i);
42229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
42329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
42429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        current += rs_blocks_per_thread;
42529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
42629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
42729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx->rv = 0;
42829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
42929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    for (int i = 0; i < threads; ++i) {
43029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (pthread_join(pthreads[i], NULL) != 0) {
43129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("failed to join thread %d: %s\n", i, strerror(errno));
43229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
43329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
43429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        ctx->rv += args[i].rv;
43529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
43629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (args[i].rs) {
43729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            free_rs_char(args[i].rs);
43829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            args[i].rs = NULL;
43929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
44029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
44129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
44229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return true;
44329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
444