1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 extern "C" {
18 #include <fec.h>
19 }
20
21 #undef NDEBUG
22
23 #include <assert.h>
24 #include <errno.h>
25 #include <getopt.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <android-base/file.h>
32 #include "image.h"
33
34 enum {
35 MODE_ENCODE,
36 MODE_DECODE,
37 MODE_PRINTSIZE,
38 MODE_GETECCSTART,
39 MODE_GETVERITYSTART
40 };
41
encode_rs(struct image_proc_ctx * ctx)42 static void encode_rs(struct image_proc_ctx *ctx)
43 {
44 struct image *fcx = ctx->ctx;
45 int j;
46 uint8_t data[fcx->rs_n];
47 uint64_t i;
48
49 for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
50 for (j = 0; j < fcx->rs_n; ++j) {
51 data[j] = image_get_interleaved_byte(i + j, fcx);
52 }
53
54 encode_rs_char(ctx->rs, data, &fcx->fec[ctx->fec_pos]);
55 ctx->fec_pos += fcx->roots;
56 }
57 }
58
decode_rs(struct image_proc_ctx * ctx)59 static void decode_rs(struct image_proc_ctx *ctx)
60 {
61 struct image *fcx = ctx->ctx;
62 int j, rv;
63 uint8_t data[fcx->rs_n + fcx->roots];
64 uint64_t i;
65
66 assert(sizeof(data) == FEC_RSM);
67
68 for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
69 for (j = 0; j < fcx->rs_n; ++j) {
70 data[j] = image_get_interleaved_byte(i + j, fcx);
71 }
72
73 memcpy(&data[fcx->rs_n], &fcx->fec[ctx->fec_pos], fcx->roots);
74 rv = decode_rs_char(ctx->rs, data, nullptr, 0);
75
76 if (rv < 0) {
77 FATAL("failed to recover [%" PRIu64 ", %" PRIu64 ")\n",
78 i, i + fcx->rs_n);
79 } else if (rv > 0) {
80 /* copy corrected data to output */
81 for (j = 0; j < fcx->rs_n; ++j) {
82 image_set_interleaved_byte(i + j, fcx, data[j]);
83 }
84
85 ctx->rv += rv;
86 }
87
88 ctx->fec_pos += fcx->roots;
89 }
90 }
91
usage()92 static int usage()
93 {
94 printf("fec: a tool for encoding and decoding files using RS(255, N).\n"
95 "\n"
96 "usage: fec <mode> [ <options> ] [ <data> <fec> [ <output> ] ]\n"
97 "mode:\n"
98 " -e --encode encode (default)\n"
99 " -d --decode decode\n"
100 " -s, --print-fec-size=<data size> print FEC size\n"
101 " -E, --get-ecc-start=data print ECC offset in data\n"
102 " -V, --get-verity-start=data print verity offset\n"
103 "options:\n"
104 " -h show this help\n"
105 " -v enable verbose logging\n"
106 " -r, --roots=<bytes> number of parity bytes\n"
107 " -j, --threads=<threads> number of threads to use\n"
108 " -S treat data as a sparse file\n"
109 "encoding options:\n"
110 " -p, --padding=<bytes> add padding after ECC data\n"
111 "decoding options:\n"
112 " -i, --inplace correct <data> in place\n"
113 );
114
115 return 1;
116 }
117
parse_arg(const char * arg,const char * name,uint64_t maxval)118 static uint64_t parse_arg(const char *arg, const char *name, uint64_t maxval)
119 {
120 char* endptr;
121 errno = 0;
122
123 unsigned long long int value = strtoull(arg, &endptr, 0);
124
125 if (arg[0] == '\0' || *endptr != '\0' ||
126 (errno == ERANGE && value == ULLONG_MAX)) {
127 FATAL("invalid value of %s\n", name);
128 }
129 if (value > maxval) {
130 FATAL("value of roots too large (max. %" PRIu64 ")\n", maxval);
131 }
132
133 return (uint64_t)value;
134 }
135
print_size(image & ctx)136 static int print_size(image& ctx)
137 {
138 /* output size including header */
139 printf("%" PRIu64 "\n", fec_ecc_get_size(ctx.inp_size, ctx.roots));
140 return 0;
141 }
142
get_start(int mode,const std::string & filename)143 static int get_start(int mode, const std::string& filename)
144 {
145 fec::io fh(filename, O_RDONLY, FEC_VERITY_DISABLE);
146
147 if (!fh) {
148 FATAL("failed to open input\n");
149 }
150
151 if (mode == MODE_GETECCSTART) {
152 fec_ecc_metadata data;
153
154 if (!fh.get_ecc_metadata(data)) {
155 FATAL("no ecc data\n");
156 }
157
158 printf("%" PRIu64 "\n", data.start);
159 } else {
160 fec_verity_metadata data;
161
162 if (!fh.get_verity_metadata(data)) {
163 FATAL("no verity data\n");
164 }
165
166 printf("%" PRIu64 "\n", data.data_size);
167 }
168
169 return 0;
170 }
171
encode(image & ctx,const std::vector<std::string> & inp_filenames,const std::string & fec_filename)172 static int encode(image& ctx, const std::vector<std::string>& inp_filenames,
173 const std::string& fec_filename)
174 {
175 if (ctx.inplace) {
176 FATAL("invalid parameters: inplace can only used when decoding\n");
177 }
178
179 if (!image_load(inp_filenames, &ctx)) {
180 FATAL("failed to read input\n");
181 }
182
183 if (!image_ecc_new(fec_filename, &ctx)) {
184 FATAL("failed to allocate ecc\n");
185 }
186
187 INFO("encoding RS(255, %d) to '%s' for input files:\n", ctx.rs_n,
188 fec_filename.c_str());
189
190 size_t n = 1;
191
192 for (const auto& fn : inp_filenames) {
193 INFO("\t%zu: '%s'\n", n++, fn.c_str());
194 }
195
196 if (ctx.verbose) {
197 INFO("\traw fec size: %u\n", ctx.fec_size);
198 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
199 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
200 }
201
202 if (!image_process(encode_rs, &ctx)) {
203 FATAL("failed to process input\n");
204 }
205
206 if (!image_ecc_save(&ctx)) {
207 FATAL("failed to write output\n");
208 }
209
210 image_free(&ctx);
211 return 0;
212 }
213
decode(image & ctx,const std::vector<std::string> & inp_filenames,const std::string & fec_filename,std::string & out_filename)214 static int decode(image& ctx, const std::vector<std::string>& inp_filenames,
215 const std::string& fec_filename, std::string& out_filename)
216 {
217 const std::string& inp_filename = inp_filenames.front();
218
219 if (ctx.inplace && ctx.sparse) {
220 FATAL("invalid parameters: inplace cannot be used with sparse "
221 "files\n");
222 }
223
224 if (ctx.padding) {
225 FATAL("invalid parameters: padding is only relevant when encoding\n");
226 }
227
228 if (!image_ecc_load(fec_filename, &ctx) ||
229 !image_load(inp_filenames, &ctx)) {
230 FATAL("failed to read input\n");
231 }
232
233 if (ctx.inplace) {
234 INFO("correcting '%s' using RS(255, %d) from '%s'\n",
235 inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
236
237 out_filename = inp_filename;
238 } else {
239 INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
240 inp_filename.c_str(),
241 out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
242 fec_filename.c_str());
243 }
244
245 if (ctx.verbose) {
246 INFO("\traw fec size: %u\n", ctx.fec_size);
247 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
248 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
249 }
250
251 if (!image_process(decode_rs, &ctx)) {
252 FATAL("failed to process input\n");
253 }
254
255 if (ctx.rv) {
256 INFO("corrected %" PRIu64 " errors\n", ctx.rv);
257 } else {
258 INFO("no errors found\n");
259 }
260
261 if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
262 FATAL("failed to write output\n");
263 }
264
265 image_free(&ctx);
266 return 0;
267 }
268
main(int argc,char ** argv)269 int main(int argc, char **argv)
270 {
271 std::string fec_filename;
272 std::string out_filename;
273 std::vector<std::string> inp_filenames;
274 int mode = MODE_ENCODE;
275 image ctx;
276
277 image_init(&ctx);
278 ctx.roots = FEC_DEFAULT_ROOTS;
279
280 while (1) {
281 const static struct option long_options[] = {
282 {"help", no_argument, nullptr, 'h'},
283 {"encode", no_argument, nullptr, 'e'},
284 {"decode", no_argument, nullptr, 'd'},
285 {"sparse", no_argument, nullptr, 'S'},
286 {"roots", required_argument, nullptr, 'r'},
287 {"inplace", no_argument, nullptr, 'i'},
288 {"threads", required_argument, nullptr, 'j'},
289 {"print-fec-size", required_argument, nullptr, 's'},
290 {"get-ecc-start", required_argument, nullptr, 'E'},
291 {"get-verity-start", required_argument, nullptr, 'V'},
292 {"padding", required_argument, nullptr, 'p'},
293 {"verbose", no_argument, nullptr, 'v'},
294 {nullptr, 0, nullptr, 0}
295 };
296 int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, nullptr);
297 if (c < 0) {
298 break;
299 }
300
301 switch (c) {
302 case 'h':
303 return usage();
304 case 'S':
305 ctx.sparse = true;
306 break;
307 case 'e':
308 if (mode != MODE_ENCODE) {
309 return usage();
310 }
311 break;
312 case 'd':
313 if (mode != MODE_ENCODE) {
314 return usage();
315 }
316 mode = MODE_DECODE;
317 break;
318 case 'r':
319 ctx.roots = (int)parse_arg(optarg, "roots", FEC_RSM);
320 break;
321 case 'i':
322 ctx.inplace = true;
323 break;
324 case 'j':
325 ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
326 break;
327 case 's':
328 if (mode != MODE_ENCODE) {
329 return usage();
330 }
331 mode = MODE_PRINTSIZE;
332 ctx.inp_size = parse_arg(optarg, "print-fec-size", UINT64_MAX);
333 break;
334 case 'E':
335 if (mode != MODE_ENCODE) {
336 return usage();
337 }
338 mode = MODE_GETECCSTART;
339 inp_filenames.push_back(optarg);
340 break;
341 case 'V':
342 if (mode != MODE_ENCODE) {
343 return usage();
344 }
345 mode = MODE_GETVERITYSTART;
346 inp_filenames.push_back(optarg);
347 break;
348 case 'p':
349 ctx.padding = (uint32_t)parse_arg(optarg, "padding", UINT32_MAX);
350 if (ctx.padding % FEC_BLOCKSIZE) {
351 FATAL("padding must be multiple of %u\n", FEC_BLOCKSIZE);
352 }
353 break;
354 case 'v':
355 ctx.verbose = true;
356 break;
357 case '?':
358 return usage();
359 default:
360 abort();
361 }
362 }
363
364 argc -= optind;
365 argv += optind;
366
367 assert(ctx.roots > 0 && ctx.roots < FEC_RSM);
368
369 /* check for input / output parameters */
370 if (mode == MODE_ENCODE) {
371 /* allow multiple input files */
372 for (int i = 0; i < (argc - 1); ++i) {
373 inp_filenames.push_back(argv[i]);
374 }
375
376 if (inp_filenames.empty()) {
377 return usage();
378 }
379
380 /* the last one is the output file */
381 fec_filename = argv[argc - 1];
382 } else if (mode == MODE_DECODE) {
383 if (argc < 2 || argc > 3) {
384 return usage();
385 } else if (argc == 3) {
386 if (ctx.inplace) {
387 return usage();
388 }
389 out_filename = argv[2];
390 }
391
392 inp_filenames.push_back(argv[0]);
393 fec_filename = argv[1];
394 }
395
396 switch (mode) {
397 case MODE_PRINTSIZE:
398 return print_size(ctx);
399 case MODE_GETECCSTART:
400 case MODE_GETVERITYSTART:
401 return get_start(mode, inp_filenames.front());
402 case MODE_ENCODE:
403 return encode(ctx, inp_filenames, fec_filename);
404 case MODE_DECODE:
405 return decode(ctx, inp_filenames, fec_filename, out_filename);
406 default:
407 abort();
408 }
409
410 return 1;
411 }
412