xref: /aosp_15_r20/system/extras/verity/fec/image.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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