1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #undef NDEBUG
18*288bf522SAndroid Build Coastguard Worker #define _LARGEFILE64_SOURCE
19*288bf522SAndroid Build Coastguard Worker
20*288bf522SAndroid Build Coastguard Worker extern "C" {
21*288bf522SAndroid Build Coastguard Worker #include <fec.h>
22*288bf522SAndroid Build Coastguard Worker }
23*288bf522SAndroid Build Coastguard Worker
24*288bf522SAndroid Build Coastguard Worker #include <assert.h>
25*288bf522SAndroid Build Coastguard Worker #include <android-base/file.h>
26*288bf522SAndroid Build Coastguard Worker #include <errno.h>
27*288bf522SAndroid Build Coastguard Worker #include <fcntl.h>
28*288bf522SAndroid Build Coastguard Worker #include <getopt.h>
29*288bf522SAndroid Build Coastguard Worker #include <openssl/sha.h>
30*288bf522SAndroid Build Coastguard Worker #include <pthread.h>
31*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
32*288bf522SAndroid Build Coastguard Worker #include <string.h>
33*288bf522SAndroid Build Coastguard Worker #include <sys/ioctl.h>
34*288bf522SAndroid Build Coastguard Worker #include <sys/mman.h>
35*288bf522SAndroid Build Coastguard Worker #include <sparse/sparse.h>
36*288bf522SAndroid Build Coastguard Worker #include "image.h"
37*288bf522SAndroid Build Coastguard Worker
38*288bf522SAndroid Build Coastguard Worker #if defined(__linux__)
39*288bf522SAndroid Build Coastguard Worker #include <linux/fs.h>
40*288bf522SAndroid Build Coastguard Worker #elif defined(__APPLE__)
41*288bf522SAndroid Build Coastguard Worker #include <sys/disk.h>
42*288bf522SAndroid Build Coastguard Worker #define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
43*288bf522SAndroid Build Coastguard Worker #define O_LARGEFILE 0
44*288bf522SAndroid Build Coastguard Worker #endif
45*288bf522SAndroid Build Coastguard Worker
image_init(image * ctx)46*288bf522SAndroid Build Coastguard Worker void image_init(image *ctx)
47*288bf522SAndroid Build Coastguard Worker {
48*288bf522SAndroid Build Coastguard Worker memset(ctx, 0, sizeof(*ctx));
49*288bf522SAndroid Build Coastguard Worker }
50*288bf522SAndroid Build Coastguard Worker
image_free(image * ctx)51*288bf522SAndroid Build Coastguard Worker void image_free(image *ctx)
52*288bf522SAndroid Build Coastguard Worker {
53*288bf522SAndroid Build Coastguard Worker assert(ctx->input == ctx->output);
54*288bf522SAndroid Build Coastguard Worker
55*288bf522SAndroid Build Coastguard Worker if (ctx->input) {
56*288bf522SAndroid Build Coastguard Worker delete[] ctx->input;
57*288bf522SAndroid Build Coastguard Worker }
58*288bf522SAndroid Build Coastguard Worker
59*288bf522SAndroid Build Coastguard Worker if (ctx->fec) {
60*288bf522SAndroid Build Coastguard Worker delete[] ctx->fec;
61*288bf522SAndroid Build Coastguard Worker }
62*288bf522SAndroid Build Coastguard Worker
63*288bf522SAndroid Build Coastguard Worker image_init(ctx);
64*288bf522SAndroid Build Coastguard Worker }
65*288bf522SAndroid Build Coastguard Worker
calculate_rounds(uint64_t size,image * ctx)66*288bf522SAndroid Build Coastguard Worker static void calculate_rounds(uint64_t size, image *ctx)
67*288bf522SAndroid Build Coastguard Worker {
68*288bf522SAndroid Build Coastguard Worker if (!size) {
69*288bf522SAndroid Build Coastguard Worker FATAL("empty file?\n");
70*288bf522SAndroid Build Coastguard Worker } else if (size % FEC_BLOCKSIZE) {
71*288bf522SAndroid Build Coastguard Worker FATAL("file size %" PRIu64 " is not a multiple of %u bytes\n",
72*288bf522SAndroid Build Coastguard Worker size, FEC_BLOCKSIZE);
73*288bf522SAndroid Build Coastguard Worker }
74*288bf522SAndroid Build Coastguard Worker
75*288bf522SAndroid Build Coastguard Worker ctx->inp_size = size;
76*288bf522SAndroid Build Coastguard Worker ctx->blocks = fec_div_round_up(ctx->inp_size, FEC_BLOCKSIZE);
77*288bf522SAndroid Build Coastguard Worker ctx->rounds = fec_div_round_up(ctx->blocks, ctx->rs_n);
78*288bf522SAndroid Build Coastguard Worker }
79*288bf522SAndroid Build Coastguard Worker
process_chunk(void * priv,const void * data,size_t len)80*288bf522SAndroid Build Coastguard Worker static int process_chunk(void *priv, const void *data, size_t len)
81*288bf522SAndroid Build Coastguard Worker {
82*288bf522SAndroid Build Coastguard Worker image *ctx = (image *)priv;
83*288bf522SAndroid Build Coastguard Worker
84*288bf522SAndroid Build Coastguard Worker if (data) {
85*288bf522SAndroid Build Coastguard Worker memcpy(&ctx->input[ctx->pos], data, len);
86*288bf522SAndroid Build Coastguard Worker }
87*288bf522SAndroid Build Coastguard Worker
88*288bf522SAndroid Build Coastguard Worker ctx->pos += len;
89*288bf522SAndroid Build Coastguard Worker return 0;
90*288bf522SAndroid Build Coastguard Worker }
91*288bf522SAndroid Build Coastguard Worker
file_image_load(const std::vector<int> & fds,image * ctx)92*288bf522SAndroid Build Coastguard Worker static void file_image_load(const std::vector<int>& fds, image *ctx)
93*288bf522SAndroid Build Coastguard Worker {
94*288bf522SAndroid Build Coastguard Worker uint64_t size = 0;
95*288bf522SAndroid Build Coastguard Worker std::vector<struct sparse_file *> files;
96*288bf522SAndroid Build Coastguard Worker
97*288bf522SAndroid Build Coastguard Worker for (auto fd : fds) {
98*288bf522SAndroid Build Coastguard Worker uint64_t len = 0;
99*288bf522SAndroid Build Coastguard Worker struct sparse_file *file;
100*288bf522SAndroid Build Coastguard Worker
101*288bf522SAndroid Build Coastguard Worker if (ctx->sparse) {
102*288bf522SAndroid Build Coastguard Worker file = sparse_file_import(fd, false, false);
103*288bf522SAndroid Build Coastguard Worker } else {
104*288bf522SAndroid Build Coastguard Worker file = sparse_file_import_auto(fd, false, ctx->verbose);
105*288bf522SAndroid Build Coastguard Worker }
106*288bf522SAndroid Build Coastguard Worker
107*288bf522SAndroid Build Coastguard Worker if (!file) {
108*288bf522SAndroid Build Coastguard Worker FATAL("failed to read file %s\n", ctx->fec_filename);
109*288bf522SAndroid Build Coastguard Worker }
110*288bf522SAndroid Build Coastguard Worker
111*288bf522SAndroid Build Coastguard Worker len = sparse_file_len(file, false, false);
112*288bf522SAndroid Build Coastguard Worker files.push_back(file);
113*288bf522SAndroid Build Coastguard Worker
114*288bf522SAndroid Build Coastguard Worker size += len;
115*288bf522SAndroid Build Coastguard Worker }
116*288bf522SAndroid Build Coastguard Worker
117*288bf522SAndroid Build Coastguard Worker calculate_rounds(size, ctx);
118*288bf522SAndroid Build Coastguard Worker
119*288bf522SAndroid Build Coastguard Worker if (ctx->verbose) {
120*288bf522SAndroid Build Coastguard Worker INFO("allocating %" PRIu64 " bytes of memory\n", ctx->inp_size);
121*288bf522SAndroid Build Coastguard Worker }
122*288bf522SAndroid Build Coastguard Worker
123*288bf522SAndroid Build Coastguard Worker ctx->input = new uint8_t[ctx->inp_size];
124*288bf522SAndroid Build Coastguard Worker
125*288bf522SAndroid Build Coastguard Worker if (!ctx->input) {
126*288bf522SAndroid Build Coastguard Worker FATAL("failed to allocate memory\n");
127*288bf522SAndroid Build Coastguard Worker }
128*288bf522SAndroid Build Coastguard Worker
129*288bf522SAndroid Build Coastguard Worker memset(ctx->input, 0, ctx->inp_size);
130*288bf522SAndroid Build Coastguard Worker ctx->output = ctx->input;
131*288bf522SAndroid Build Coastguard Worker ctx->pos = 0;
132*288bf522SAndroid Build Coastguard Worker
133*288bf522SAndroid Build Coastguard Worker for (auto file : files) {
134*288bf522SAndroid Build Coastguard Worker sparse_file_callback(file, false, false, process_chunk, ctx);
135*288bf522SAndroid Build Coastguard Worker sparse_file_destroy(file);
136*288bf522SAndroid Build Coastguard Worker }
137*288bf522SAndroid Build Coastguard Worker
138*288bf522SAndroid Build Coastguard Worker assert(ctx->pos % FEC_BLOCKSIZE == 0);
139*288bf522SAndroid Build Coastguard Worker
140*288bf522SAndroid Build Coastguard Worker for (auto fd : fds) {
141*288bf522SAndroid Build Coastguard Worker close(fd);
142*288bf522SAndroid Build Coastguard Worker }
143*288bf522SAndroid Build Coastguard Worker }
144*288bf522SAndroid Build Coastguard Worker
image_load(const std::vector<std::string> & filenames,image * ctx)145*288bf522SAndroid Build Coastguard Worker bool image_load(const std::vector<std::string>& filenames, image *ctx)
146*288bf522SAndroid Build Coastguard Worker {
147*288bf522SAndroid Build Coastguard Worker assert(ctx->roots > 0 && ctx->roots < FEC_RSM);
148*288bf522SAndroid Build Coastguard Worker ctx->rs_n = FEC_RSM - ctx->roots;
149*288bf522SAndroid Build Coastguard Worker
150*288bf522SAndroid Build Coastguard Worker int flags = O_RDONLY;
151*288bf522SAndroid Build Coastguard Worker
152*288bf522SAndroid Build Coastguard Worker if (ctx->inplace) {
153*288bf522SAndroid Build Coastguard Worker flags = O_RDWR;
154*288bf522SAndroid Build Coastguard Worker }
155*288bf522SAndroid Build Coastguard Worker
156*288bf522SAndroid Build Coastguard Worker std::vector<int> fds;
157*288bf522SAndroid Build Coastguard Worker
158*288bf522SAndroid Build Coastguard Worker for (const auto& fn : filenames) {
159*288bf522SAndroid Build Coastguard Worker int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), flags | O_LARGEFILE));
160*288bf522SAndroid Build Coastguard Worker
161*288bf522SAndroid Build Coastguard Worker if (fd < 0) {
162*288bf522SAndroid Build Coastguard Worker FATAL("failed to open file '%s': %s\n", fn.c_str(), strerror(errno));
163*288bf522SAndroid Build Coastguard Worker }
164*288bf522SAndroid Build Coastguard Worker
165*288bf522SAndroid Build Coastguard Worker fds.push_back(fd);
166*288bf522SAndroid Build Coastguard Worker }
167*288bf522SAndroid Build Coastguard Worker
168*288bf522SAndroid Build Coastguard Worker file_image_load(fds, ctx);
169*288bf522SAndroid Build Coastguard Worker
170*288bf522SAndroid Build Coastguard Worker return true;
171*288bf522SAndroid Build Coastguard Worker }
172*288bf522SAndroid Build Coastguard Worker
image_save(const std::string & filename,image * ctx)173*288bf522SAndroid Build Coastguard Worker bool image_save(const std::string& filename, image *ctx)
174*288bf522SAndroid Build Coastguard Worker {
175*288bf522SAndroid Build Coastguard Worker /* TODO: support saving as a sparse file */
176*288bf522SAndroid Build Coastguard Worker int fd = TEMP_FAILURE_RETRY(open(filename.c_str(),
177*288bf522SAndroid Build Coastguard Worker O_WRONLY | O_CREAT | O_TRUNC, 0666));
178*288bf522SAndroid Build Coastguard Worker
179*288bf522SAndroid Build Coastguard Worker if (fd < 0) {
180*288bf522SAndroid Build Coastguard Worker FATAL("failed to open file '%s: %s'\n", filename.c_str(),
181*288bf522SAndroid Build Coastguard Worker strerror(errno));
182*288bf522SAndroid Build Coastguard Worker }
183*288bf522SAndroid Build Coastguard Worker
184*288bf522SAndroid Build Coastguard Worker if (!android::base::WriteFully(fd, ctx->output, ctx->inp_size)) {
185*288bf522SAndroid Build Coastguard Worker FATAL("failed to write to output: %s\n", strerror(errno));
186*288bf522SAndroid Build Coastguard Worker }
187*288bf522SAndroid Build Coastguard Worker
188*288bf522SAndroid Build Coastguard Worker close(fd);
189*288bf522SAndroid Build Coastguard Worker return true;
190*288bf522SAndroid Build Coastguard Worker }
191*288bf522SAndroid Build Coastguard Worker
image_ecc_new(const std::string & filename,image * ctx)192*288bf522SAndroid Build Coastguard Worker bool image_ecc_new(const std::string& filename, image *ctx)
193*288bf522SAndroid Build Coastguard Worker {
194*288bf522SAndroid Build Coastguard Worker assert(ctx->rounds > 0); /* image_load should be called first */
195*288bf522SAndroid Build Coastguard Worker
196*288bf522SAndroid Build Coastguard Worker ctx->fec_filename = filename.c_str();
197*288bf522SAndroid Build Coastguard Worker ctx->fec_size = ctx->rounds * ctx->roots * FEC_BLOCKSIZE;
198*288bf522SAndroid Build Coastguard Worker
199*288bf522SAndroid Build Coastguard Worker if (ctx->verbose) {
200*288bf522SAndroid Build Coastguard Worker INFO("allocating %u bytes of memory\n", ctx->fec_size);
201*288bf522SAndroid Build Coastguard Worker }
202*288bf522SAndroid Build Coastguard Worker
203*288bf522SAndroid Build Coastguard Worker ctx->fec = new uint8_t[ctx->fec_size];
204*288bf522SAndroid Build Coastguard Worker
205*288bf522SAndroid Build Coastguard Worker if (!ctx->fec) {
206*288bf522SAndroid Build Coastguard Worker FATAL("failed to allocate %u bytes\n", ctx->fec_size);
207*288bf522SAndroid Build Coastguard Worker }
208*288bf522SAndroid Build Coastguard Worker
209*288bf522SAndroid Build Coastguard Worker return true;
210*288bf522SAndroid Build Coastguard Worker }
211*288bf522SAndroid Build Coastguard Worker
image_ecc_load(const std::string & filename,image * ctx)212*288bf522SAndroid Build Coastguard Worker bool image_ecc_load(const std::string& filename, image *ctx)
213*288bf522SAndroid Build Coastguard Worker {
214*288bf522SAndroid Build Coastguard Worker int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
215*288bf522SAndroid Build Coastguard Worker
216*288bf522SAndroid Build Coastguard Worker if (fd < 0) {
217*288bf522SAndroid Build Coastguard Worker FATAL("failed to open file '%s': %s\n", filename.c_str(),
218*288bf522SAndroid Build Coastguard Worker strerror(errno));
219*288bf522SAndroid Build Coastguard Worker }
220*288bf522SAndroid Build Coastguard Worker
221*288bf522SAndroid Build Coastguard Worker if (lseek64(fd, -FEC_BLOCKSIZE, SEEK_END) < 0) {
222*288bf522SAndroid Build Coastguard Worker FATAL("failed to seek to header in '%s': %s\n", filename.c_str(),
223*288bf522SAndroid Build Coastguard Worker strerror(errno));
224*288bf522SAndroid Build Coastguard Worker }
225*288bf522SAndroid Build Coastguard Worker
226*288bf522SAndroid Build Coastguard Worker assert(sizeof(fec_header) <= FEC_BLOCKSIZE);
227*288bf522SAndroid Build Coastguard Worker
228*288bf522SAndroid Build Coastguard Worker uint8_t header[FEC_BLOCKSIZE];
229*288bf522SAndroid Build Coastguard Worker fec_header *p = (fec_header *)header;
230*288bf522SAndroid Build Coastguard Worker
231*288bf522SAndroid Build Coastguard Worker if (!android::base::ReadFully(fd, header, sizeof(header))) {
232*288bf522SAndroid Build Coastguard Worker FATAL("failed to read %zd bytes from '%s': %s\n", sizeof(header),
233*288bf522SAndroid Build Coastguard Worker filename.c_str(), strerror(errno));
234*288bf522SAndroid Build Coastguard Worker }
235*288bf522SAndroid Build Coastguard Worker
236*288bf522SAndroid Build Coastguard Worker if (p->magic != FEC_MAGIC) {
237*288bf522SAndroid Build Coastguard Worker FATAL("invalid magic in '%s': %08x\n", filename.c_str(), p->magic);
238*288bf522SAndroid Build Coastguard Worker }
239*288bf522SAndroid Build Coastguard Worker
240*288bf522SAndroid Build Coastguard Worker if (p->version != FEC_VERSION) {
241*288bf522SAndroid Build Coastguard Worker FATAL("unsupported version in '%s': %u\n", filename.c_str(),
242*288bf522SAndroid Build Coastguard Worker p->version);
243*288bf522SAndroid Build Coastguard Worker }
244*288bf522SAndroid Build Coastguard Worker
245*288bf522SAndroid Build Coastguard Worker if (p->size != sizeof(fec_header)) {
246*288bf522SAndroid Build Coastguard Worker FATAL("unexpected header size in '%s': %u\n", filename.c_str(),
247*288bf522SAndroid Build Coastguard Worker p->size);
248*288bf522SAndroid Build Coastguard Worker }
249*288bf522SAndroid Build Coastguard Worker
250*288bf522SAndroid Build Coastguard Worker if (p->roots == 0 || p->roots >= FEC_RSM) {
251*288bf522SAndroid Build Coastguard Worker FATAL("invalid roots in '%s': %u\n", filename.c_str(), p->roots);
252*288bf522SAndroid Build Coastguard Worker }
253*288bf522SAndroid Build Coastguard Worker
254*288bf522SAndroid Build Coastguard Worker if (p->fec_size % p->roots || p->fec_size % FEC_BLOCKSIZE) {
255*288bf522SAndroid Build Coastguard Worker FATAL("invalid length in '%s': %u\n", filename.c_str(), p->fec_size);
256*288bf522SAndroid Build Coastguard Worker }
257*288bf522SAndroid Build Coastguard Worker
258*288bf522SAndroid Build Coastguard Worker ctx->roots = (int)p->roots;
259*288bf522SAndroid Build Coastguard Worker ctx->rs_n = FEC_RSM - ctx->roots;
260*288bf522SAndroid Build Coastguard Worker
261*288bf522SAndroid Build Coastguard Worker calculate_rounds(p->inp_size, ctx);
262*288bf522SAndroid Build Coastguard Worker
263*288bf522SAndroid Build Coastguard Worker if (!image_ecc_new(filename, ctx)) {
264*288bf522SAndroid Build Coastguard Worker FATAL("failed to allocate ecc\n");
265*288bf522SAndroid Build Coastguard Worker }
266*288bf522SAndroid Build Coastguard Worker
267*288bf522SAndroid Build Coastguard Worker if (p->fec_size != ctx->fec_size) {
268*288bf522SAndroid Build Coastguard Worker FATAL("inconsistent header in '%s'\n", filename.c_str());
269*288bf522SAndroid Build Coastguard Worker }
270*288bf522SAndroid Build Coastguard Worker
271*288bf522SAndroid Build Coastguard Worker if (lseek64(fd, 0, SEEK_SET) < 0) {
272*288bf522SAndroid Build Coastguard Worker FATAL("failed to rewind '%s': %s", filename.c_str(), strerror(errno));
273*288bf522SAndroid Build Coastguard Worker }
274*288bf522SAndroid Build Coastguard Worker
275*288bf522SAndroid Build Coastguard Worker if (!android::base::ReadFully(fd, ctx->fec, ctx->fec_size)) {
276*288bf522SAndroid Build Coastguard Worker FATAL("failed to read %u bytes from '%s': %s\n", ctx->fec_size,
277*288bf522SAndroid Build Coastguard Worker filename.c_str(), strerror(errno));
278*288bf522SAndroid Build Coastguard Worker }
279*288bf522SAndroid Build Coastguard Worker
280*288bf522SAndroid Build Coastguard Worker close(fd);
281*288bf522SAndroid Build Coastguard Worker
282*288bf522SAndroid Build Coastguard Worker uint8_t hash[SHA256_DIGEST_LENGTH];
283*288bf522SAndroid Build Coastguard Worker SHA256(ctx->fec, ctx->fec_size, hash);
284*288bf522SAndroid Build Coastguard Worker
285*288bf522SAndroid Build Coastguard Worker if (memcmp(hash, p->hash, SHA256_DIGEST_LENGTH) != 0) {
286*288bf522SAndroid Build Coastguard Worker FATAL("invalid ecc data\n");
287*288bf522SAndroid Build Coastguard Worker }
288*288bf522SAndroid Build Coastguard Worker
289*288bf522SAndroid Build Coastguard Worker return true;
290*288bf522SAndroid Build Coastguard Worker }
291*288bf522SAndroid Build Coastguard Worker
image_ecc_save(image * ctx)292*288bf522SAndroid Build Coastguard Worker bool image_ecc_save(image *ctx)
293*288bf522SAndroid Build Coastguard Worker {
294*288bf522SAndroid Build Coastguard Worker assert(2 * sizeof(fec_header) <= FEC_BLOCKSIZE);
295*288bf522SAndroid Build Coastguard Worker
296*288bf522SAndroid Build Coastguard Worker uint8_t header[FEC_BLOCKSIZE] = {0};
297*288bf522SAndroid Build Coastguard Worker
298*288bf522SAndroid Build Coastguard Worker fec_header *f = (fec_header *)header;
299*288bf522SAndroid Build Coastguard Worker
300*288bf522SAndroid Build Coastguard Worker f->magic = FEC_MAGIC;
301*288bf522SAndroid Build Coastguard Worker f->version = FEC_VERSION;
302*288bf522SAndroid Build Coastguard Worker f->size = sizeof(fec_header);
303*288bf522SAndroid Build Coastguard Worker f->roots = ctx->roots;
304*288bf522SAndroid Build Coastguard Worker f->fec_size = ctx->fec_size;
305*288bf522SAndroid Build Coastguard Worker f->inp_size = ctx->inp_size;
306*288bf522SAndroid Build Coastguard Worker
307*288bf522SAndroid Build Coastguard Worker SHA256(ctx->fec, ctx->fec_size, f->hash);
308*288bf522SAndroid Build Coastguard Worker
309*288bf522SAndroid Build Coastguard Worker /* store a copy of the fec_header at the end of the header block */
310*288bf522SAndroid Build Coastguard Worker memcpy(&header[sizeof(header) - sizeof(fec_header)], header,
311*288bf522SAndroid Build Coastguard Worker sizeof(fec_header));
312*288bf522SAndroid Build Coastguard Worker
313*288bf522SAndroid Build Coastguard Worker assert(ctx->fec_filename);
314*288bf522SAndroid Build Coastguard Worker
315*288bf522SAndroid Build Coastguard Worker int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
316*288bf522SAndroid Build Coastguard Worker O_WRONLY | O_CREAT | O_TRUNC, 0666));
317*288bf522SAndroid Build Coastguard Worker
318*288bf522SAndroid Build Coastguard Worker if (fd < 0) {
319*288bf522SAndroid Build Coastguard Worker FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
320*288bf522SAndroid Build Coastguard Worker strerror(errno));
321*288bf522SAndroid Build Coastguard Worker }
322*288bf522SAndroid Build Coastguard Worker
323*288bf522SAndroid Build Coastguard Worker if (!android::base::WriteFully(fd, ctx->fec, ctx->fec_size)) {
324*288bf522SAndroid Build Coastguard Worker FATAL("failed to write to output: %s\n", strerror(errno));
325*288bf522SAndroid Build Coastguard Worker }
326*288bf522SAndroid Build Coastguard Worker
327*288bf522SAndroid Build Coastguard Worker if (ctx->padding > 0) {
328*288bf522SAndroid Build Coastguard Worker uint8_t padding[FEC_BLOCKSIZE] = {0};
329*288bf522SAndroid Build Coastguard Worker
330*288bf522SAndroid Build Coastguard Worker for (uint32_t i = 0; i < ctx->padding; i += FEC_BLOCKSIZE) {
331*288bf522SAndroid Build Coastguard Worker if (!android::base::WriteFully(fd, padding, FEC_BLOCKSIZE)) {
332*288bf522SAndroid Build Coastguard Worker FATAL("failed to write padding: %s\n", strerror(errno));
333*288bf522SAndroid Build Coastguard Worker }
334*288bf522SAndroid Build Coastguard Worker }
335*288bf522SAndroid Build Coastguard Worker }
336*288bf522SAndroid Build Coastguard Worker
337*288bf522SAndroid Build Coastguard Worker if (!android::base::WriteFully(fd, header, sizeof(header))) {
338*288bf522SAndroid Build Coastguard Worker FATAL("failed to write to header: %s\n", strerror(errno));
339*288bf522SAndroid Build Coastguard Worker }
340*288bf522SAndroid Build Coastguard Worker
341*288bf522SAndroid Build Coastguard Worker close(fd);
342*288bf522SAndroid Build Coastguard Worker
343*288bf522SAndroid Build Coastguard Worker return true;
344*288bf522SAndroid Build Coastguard Worker }
345*288bf522SAndroid Build Coastguard Worker
process(void * cookie)346*288bf522SAndroid Build Coastguard Worker static void * process(void *cookie)
347*288bf522SAndroid Build Coastguard Worker {
348*288bf522SAndroid Build Coastguard Worker image_proc_ctx *ctx = (image_proc_ctx *)cookie;
349*288bf522SAndroid Build Coastguard Worker ctx->func(ctx);
350*288bf522SAndroid Build Coastguard Worker return nullptr;
351*288bf522SAndroid Build Coastguard Worker }
352*288bf522SAndroid Build Coastguard Worker
image_process(image_proc_func func,image * ctx)353*288bf522SAndroid Build Coastguard Worker bool image_process(image_proc_func func, image *ctx)
354*288bf522SAndroid Build Coastguard Worker {
355*288bf522SAndroid Build Coastguard Worker int threads = ctx->threads;
356*288bf522SAndroid Build Coastguard Worker
357*288bf522SAndroid Build Coastguard Worker if (threads < IMAGE_MIN_THREADS) {
358*288bf522SAndroid Build Coastguard Worker threads = sysconf(_SC_NPROCESSORS_ONLN);
359*288bf522SAndroid Build Coastguard Worker
360*288bf522SAndroid Build Coastguard Worker if (threads < IMAGE_MIN_THREADS) {
361*288bf522SAndroid Build Coastguard Worker threads = IMAGE_MIN_THREADS;
362*288bf522SAndroid Build Coastguard Worker }
363*288bf522SAndroid Build Coastguard Worker }
364*288bf522SAndroid Build Coastguard Worker
365*288bf522SAndroid Build Coastguard Worker assert(ctx->rounds > 0);
366*288bf522SAndroid Build Coastguard Worker
367*288bf522SAndroid Build Coastguard Worker if ((uint64_t)threads > ctx->rounds) {
368*288bf522SAndroid Build Coastguard Worker threads = (int)ctx->rounds;
369*288bf522SAndroid Build Coastguard Worker }
370*288bf522SAndroid Build Coastguard Worker if (threads > IMAGE_MAX_THREADS) {
371*288bf522SAndroid Build Coastguard Worker threads = IMAGE_MAX_THREADS;
372*288bf522SAndroid Build Coastguard Worker }
373*288bf522SAndroid Build Coastguard Worker
374*288bf522SAndroid Build Coastguard Worker if (ctx->verbose) {
375*288bf522SAndroid Build Coastguard Worker INFO("starting %d threads to compute RS(255, %d)\n", threads,
376*288bf522SAndroid Build Coastguard Worker ctx->rs_n);
377*288bf522SAndroid Build Coastguard Worker }
378*288bf522SAndroid Build Coastguard Worker
379*288bf522SAndroid Build Coastguard Worker pthread_t pthreads[threads];
380*288bf522SAndroid Build Coastguard Worker image_proc_ctx args[threads];
381*288bf522SAndroid Build Coastguard Worker
382*288bf522SAndroid Build Coastguard Worker uint64_t current = 0;
383*288bf522SAndroid Build Coastguard Worker uint64_t end = ctx->rounds * ctx->rs_n * FEC_BLOCKSIZE;
384*288bf522SAndroid Build Coastguard Worker uint64_t rs_blocks_per_thread =
385*288bf522SAndroid Build Coastguard Worker fec_div_round_up(ctx->rounds * FEC_BLOCKSIZE, threads);
386*288bf522SAndroid Build Coastguard Worker
387*288bf522SAndroid Build Coastguard Worker if (ctx->verbose) {
388*288bf522SAndroid Build Coastguard Worker INFO("computing %" PRIu64 " codes per thread\n", rs_blocks_per_thread);
389*288bf522SAndroid Build Coastguard Worker }
390*288bf522SAndroid Build Coastguard Worker
391*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < threads; ++i) {
392*288bf522SAndroid Build Coastguard Worker args[i].func = func;
393*288bf522SAndroid Build Coastguard Worker args[i].id = i;
394*288bf522SAndroid Build Coastguard Worker args[i].ctx = ctx;
395*288bf522SAndroid Build Coastguard Worker args[i].rv = 0;
396*288bf522SAndroid Build Coastguard Worker args[i].fec_pos = current * ctx->roots;
397*288bf522SAndroid Build Coastguard Worker args[i].start = current * ctx->rs_n;
398*288bf522SAndroid Build Coastguard Worker args[i].end = (current + rs_blocks_per_thread) * ctx->rs_n;
399*288bf522SAndroid Build Coastguard Worker
400*288bf522SAndroid Build Coastguard Worker args[i].rs = init_rs_char(FEC_PARAMS(ctx->roots));
401*288bf522SAndroid Build Coastguard Worker
402*288bf522SAndroid Build Coastguard Worker if (!args[i].rs) {
403*288bf522SAndroid Build Coastguard Worker FATAL("failed to initialize encoder for thread %d\n", i);
404*288bf522SAndroid Build Coastguard Worker }
405*288bf522SAndroid Build Coastguard Worker
406*288bf522SAndroid Build Coastguard Worker if (args[i].end > end) {
407*288bf522SAndroid Build Coastguard Worker args[i].end = end;
408*288bf522SAndroid Build Coastguard Worker } else if (i == threads && args[i].end + rs_blocks_per_thread *
409*288bf522SAndroid Build Coastguard Worker ctx->rs_n > end) {
410*288bf522SAndroid Build Coastguard Worker args[i].end = end;
411*288bf522SAndroid Build Coastguard Worker }
412*288bf522SAndroid Build Coastguard Worker
413*288bf522SAndroid Build Coastguard Worker if (ctx->verbose) {
414*288bf522SAndroid Build Coastguard Worker INFO("thread %d: [%" PRIu64 ", %" PRIu64 ")\n",
415*288bf522SAndroid Build Coastguard Worker i, args[i].start, args[i].end);
416*288bf522SAndroid Build Coastguard Worker }
417*288bf522SAndroid Build Coastguard Worker
418*288bf522SAndroid Build Coastguard Worker assert(args[i].start < args[i].end);
419*288bf522SAndroid Build Coastguard Worker assert((args[i].end - args[i].start) % ctx->rs_n == 0);
420*288bf522SAndroid Build Coastguard Worker
421*288bf522SAndroid Build Coastguard Worker if (pthread_create(&pthreads[i], nullptr, process, &args[i]) != 0) {
422*288bf522SAndroid Build Coastguard Worker FATAL("failed to create thread %d\n", i);
423*288bf522SAndroid Build Coastguard Worker }
424*288bf522SAndroid Build Coastguard Worker
425*288bf522SAndroid Build Coastguard Worker current += rs_blocks_per_thread;
426*288bf522SAndroid Build Coastguard Worker }
427*288bf522SAndroid Build Coastguard Worker
428*288bf522SAndroid Build Coastguard Worker ctx->rv = 0;
429*288bf522SAndroid Build Coastguard Worker
430*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < threads; ++i) {
431*288bf522SAndroid Build Coastguard Worker if (pthread_join(pthreads[i], nullptr) != 0) {
432*288bf522SAndroid Build Coastguard Worker FATAL("failed to join thread %d: %s\n", i, strerror(errno));
433*288bf522SAndroid Build Coastguard Worker }
434*288bf522SAndroid Build Coastguard Worker
435*288bf522SAndroid Build Coastguard Worker ctx->rv += args[i].rv;
436*288bf522SAndroid Build Coastguard Worker
437*288bf522SAndroid Build Coastguard Worker if (args[i].rs) {
438*288bf522SAndroid Build Coastguard Worker free_rs_char(args[i].rs);
439*288bf522SAndroid Build Coastguard Worker args[i].rs = nullptr;
440*288bf522SAndroid Build Coastguard Worker }
441*288bf522SAndroid Build Coastguard Worker }
442*288bf522SAndroid Build Coastguard Worker
443*288bf522SAndroid Build Coastguard Worker return true;
444*288bf522SAndroid Build Coastguard Worker }
445