blob: 93f1ec2a25faefa7024fc0796cad65ba1cbc3ef2 [file] [log] [blame]
Sami Tolvanen29bf7372015-05-16 15:14:14 +01001/*
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
17extern "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 <stdbool.h>
29#include <stdlib.h>
30#include <string.h>
Sami Tolvanenf35a15d2015-12-18 15:28:06 +000031
Elliott Hughes66dd09e2015-12-04 14:00:57 -080032#include <android-base/file.h>
Sami Tolvanen29bf7372015-05-16 15:14:14 +010033#include "image.h"
34
35enum {
36 MODE_ENCODE,
37 MODE_DECODE,
38 MODE_PRINTSIZE,
39 MODE_GETECCSTART,
40 MODE_GETVERITYSTART
41};
42
43static void encode_rs(struct image_proc_ctx *ctx)
44{
45 struct image *fcx = ctx->ctx;
46 int j;
47 uint8_t data[fcx->rs_n];
48 uint64_t i;
49
50 for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
51 for (j = 0; j < fcx->rs_n; ++j) {
52 data[j] = image_get_interleaved_byte(i + j, fcx);
53 }
54
55 encode_rs_char(ctx->rs, data, &fcx->fec[ctx->fec_pos]);
56 ctx->fec_pos += fcx->roots;
57 }
58}
59
60static void decode_rs(struct image_proc_ctx *ctx)
61{
62 struct image *fcx = ctx->ctx;
63 int j, rv;
64 uint8_t data[fcx->rs_n + fcx->roots];
65 uint64_t i;
66
67 assert(sizeof(data) == FEC_RSM);
68
69 for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
70 for (j = 0; j < fcx->rs_n; ++j) {
71 data[j] = image_get_interleaved_byte(i + j, fcx);
72 }
73
74 memcpy(&data[fcx->rs_n], &fcx->fec[ctx->fec_pos], fcx->roots);
75 rv = decode_rs_char(ctx->rs, data, NULL, 0);
76
77 if (rv < 0) {
78 FATAL("failed to recover [%" PRIu64 ", %" PRIu64 ")\n",
79 i, i + fcx->rs_n);
80 } else if (rv > 0) {
81 /* copy corrected data to output */
82 for (j = 0; j < fcx->rs_n; ++j) {
83 image_set_interleaved_byte(i + j, fcx, data[j]);
84 }
85
86 ctx->rv += rv;
87 }
88
89 ctx->fec_pos += fcx->roots;
90 }
91}
92
93static int usage()
94{
95 printf("fec: a tool for encoding and decoding files using RS(255, N).\n"
96 "\n"
97 "usage: fec <mode> [ <options> ] [ <data> <fec> [ <output> ] ]\n"
98 "mode:\n"
99 " -e --encode encode (default)\n"
100 " -d --decode decode\n"
101 " -s, --print-fec-size=<data size> print FEC size\n"
102 " -E, --get-ecc-start=data print ECC offset in data\n"
103 " -V, --get-verity-start=data print verity offset\n"
104 "options:\n"
105 " -h show this help\n"
106 " -v enable verbose logging\n"
107 " -r, --roots=<bytes> number of parity bytes\n"
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100108 " -j, --threads=<threads> number of threads to use\n"
109 " -S treat data as a sparse file\n"
Sami Tolvanen85e592c2016-09-01 14:54:09 -0700110 "encoding options:\n"
111 " -p, --padding=<bytes> add padding after ECC data\n"
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100112 "decoding options:\n"
113 " -i, --inplace correct <data> in place\n"
114 );
115
116 return 1;
117}
118
119static uint64_t parse_arg(const char *arg, const char *name, uint64_t maxval)
120{
121 char* endptr;
122 errno = 0;
123
124 unsigned long long int value = strtoull(arg, &endptr, 0);
125
126 if (arg[0] == '\0' || *endptr != '\0' ||
127 (errno == ERANGE && value == ULLONG_MAX)) {
128 FATAL("invalid value of %s\n", name);
129 }
130 if (value > maxval) {
131 FATAL("value of roots too large (max. %" PRIu64 ")\n", maxval);
132 }
133
134 return (uint64_t)value;
135}
136
137static int print_size(image& ctx)
138{
139 /* output size including header */
140 printf("%" PRIu64 "\n", fec_ecc_get_size(ctx.inp_size, ctx.roots));
141 return 0;
142}
143
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000144static int get_start(int mode, const std::string& filename)
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100145{
146 fec::io fh(filename, O_RDONLY, FEC_VERITY_DISABLE);
147
148 if (!fh) {
149 FATAL("failed to open input\n");
150 }
151
152 if (mode == MODE_GETECCSTART) {
153 fec_ecc_metadata data;
154
155 if (!fh.get_ecc_metadata(data)) {
156 FATAL("no ecc data\n");
157 }
158
Sami Tolvanen4b71c1b2015-11-18 10:13:45 +0000159 printf("%" PRIu64 "\n", data.start);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100160 } else {
161 fec_verity_metadata data;
162
163 if (!fh.get_verity_metadata(data)) {
164 FATAL("no verity data\n");
165 }
166
167 printf("%" PRIu64 "\n", data.data_size);
168 }
169
170 return 0;
171}
172
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000173static int encode(image& ctx, const std::vector<std::string>& inp_filenames,
174 const std::string& fec_filename)
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100175{
176 if (ctx.inplace) {
177 FATAL("invalid parameters: inplace can only used when decoding\n");
178 }
179
Sami Tolvanen8bad8272016-09-01 13:35:17 -0700180 if (!image_load(inp_filenames, &ctx)) {
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100181 FATAL("failed to read input\n");
182 }
183
184 if (!image_ecc_new(fec_filename, &ctx)) {
185 FATAL("failed to allocate ecc\n");
186 }
187
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000188 INFO("encoding RS(255, %d) to '%s' for input files:\n", ctx.rs_n,
189 fec_filename.c_str());
190
191 size_t n = 1;
192
Chih-Hung Hsieha3b37f42016-07-27 15:06:58 -0700193 for (const auto& fn : inp_filenames) {
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000194 INFO("\t%zu: '%s'\n", n++, fn.c_str());
195 }
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100196
197 if (ctx.verbose) {
198 INFO("\traw fec size: %u\n", ctx.fec_size);
199 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
200 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
201 }
202
203 if (!image_process(encode_rs, &ctx)) {
204 FATAL("failed to process input\n");
205 }
206
207 if (!image_ecc_save(&ctx)) {
208 FATAL("failed to write output\n");
209 }
210
211 image_free(&ctx);
212 return 0;
213}
214
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000215static int decode(image& ctx, const std::vector<std::string>& inp_filenames,
216 const std::string& fec_filename, std::string& out_filename)
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100217{
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000218 const std::string& inp_filename = inp_filenames.front();
219
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100220 if (ctx.inplace && ctx.sparse) {
221 FATAL("invalid parameters: inplace cannot be used with sparse "
222 "files\n");
223 }
224
Sami Tolvanen85e592c2016-09-01 14:54:09 -0700225 if (ctx.padding) {
226 FATAL("invalid parameters: padding is only relevant when encoding\n");
227 }
228
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100229 if (!image_ecc_load(fec_filename, &ctx) ||
Sami Tolvanen8bad8272016-09-01 13:35:17 -0700230 !image_load(inp_filenames, &ctx)) {
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100231 FATAL("failed to read input\n");
232 }
233
234 if (ctx.inplace) {
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000235 INFO("correcting '%s' using RS(255, %d) from '%s'\n",
236 inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100237
238 out_filename = inp_filename;
239 } else {
240 INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000241 inp_filename.c_str(),
242 out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
243 fec_filename.c_str());
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100244 }
245
246 if (ctx.verbose) {
247 INFO("\traw fec size: %u\n", ctx.fec_size);
248 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
249 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
250 }
251
252 if (!image_process(decode_rs, &ctx)) {
253 FATAL("failed to process input\n");
254 }
255
256 if (ctx.rv) {
257 INFO("corrected %" PRIu64 " errors\n", ctx.rv);
258 } else {
259 INFO("no errors found\n");
260 }
261
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000262 if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100263 FATAL("failed to write output\n");
264 }
265
266 image_free(&ctx);
267 return 0;
268}
269
270int main(int argc, char **argv)
271{
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000272 std::string fec_filename;
273 std::string out_filename;
274 std::vector<std::string> inp_filenames;
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100275 int mode = MODE_ENCODE;
276 image ctx;
277
278 image_init(&ctx);
279 ctx.roots = FEC_DEFAULT_ROOTS;
280
281 while (1) {
282 const static struct option long_options[] = {
283 {"help", no_argument, 0, 'h'},
284 {"encode", no_argument, 0, 'e'},
285 {"decode", no_argument, 0, 'd'},
286 {"sparse", no_argument, 0, 'S'},
287 {"roots", required_argument, 0, 'r'},
288 {"inplace", no_argument, 0, 'i'},
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100289 {"threads", required_argument, 0, 'j'},
290 {"print-fec-size", required_argument, 0, 's'},
291 {"get-ecc-start", required_argument, 0, 'E'},
292 {"get-verity-start", required_argument, 0, 'V'},
Sami Tolvanen85e592c2016-09-01 14:54:09 -0700293 {"padding", required_argument, 0, 'p'},
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100294 {"verbose", no_argument, 0, 'v'},
295 {NULL, 0, 0, 0}
296 };
Sami Tolvanen85e592c2016-09-01 14:54:09 -0700297 int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, NULL);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100298 if (c < 0) {
299 break;
300 }
301
302 switch (c) {
303 case 'h':
304 return usage();
305 case 'S':
306 ctx.sparse = true;
307 break;
308 case 'e':
309 if (mode != MODE_ENCODE) {
310 return usage();
311 }
312 break;
313 case 'd':
314 if (mode != MODE_ENCODE) {
315 return usage();
316 }
317 mode = MODE_DECODE;
318 break;
319 case 'r':
320 ctx.roots = (int)parse_arg(optarg, "roots", FEC_RSM);
321 break;
322 case 'i':
323 ctx.inplace = true;
324 break;
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100325 case 'j':
326 ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
327 break;
328 case 's':
329 if (mode != MODE_ENCODE) {
330 return usage();
331 }
332 mode = MODE_PRINTSIZE;
333 ctx.inp_size = parse_arg(optarg, "print-fec-size", UINT64_MAX);
334 break;
335 case 'E':
336 if (mode != MODE_ENCODE) {
337 return usage();
338 }
339 mode = MODE_GETECCSTART;
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000340 inp_filenames.push_back(optarg);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100341 break;
342 case 'V':
343 if (mode != MODE_ENCODE) {
344 return usage();
345 }
346 mode = MODE_GETVERITYSTART;
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000347 inp_filenames.push_back(optarg);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100348 break;
Sami Tolvanen85e592c2016-09-01 14:54:09 -0700349 case 'p':
350 ctx.padding = (uint32_t)parse_arg(optarg, "padding", UINT32_MAX);
351 if (ctx.padding % FEC_BLOCKSIZE) {
352 FATAL("padding must be multiple of %u\n", FEC_BLOCKSIZE);
353 }
354 break;
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100355 case 'v':
356 ctx.verbose = true;
357 break;
358 case '?':
359 return usage();
360 default:
361 abort();
362 }
363 }
364
365 argc -= optind;
366 argv += optind;
367
368 assert(ctx.roots > 0 && ctx.roots < FEC_RSM);
369
370 /* check for input / output parameters */
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000371 if (mode == MODE_ENCODE) {
372 /* allow multiple input files */
373 for (int i = 0; i < (argc - 1); ++i) {
374 inp_filenames.push_back(argv[i]);
375 }
376
377 if (inp_filenames.empty()) {
378 return usage();
379 }
380
381 /* the last one is the output file */
382 fec_filename = argv[argc - 1];
383 } else if (mode == MODE_DECODE) {
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100384 if (argc < 2 || argc > 3) {
385 return usage();
386 } else if (argc == 3) {
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000387 if (ctx.inplace) {
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100388 return usage();
389 }
390 out_filename = argv[2];
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100391 }
392
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000393 inp_filenames.push_back(argv[0]);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100394 fec_filename = argv[1];
395 }
396
397 switch (mode) {
398 case MODE_PRINTSIZE:
399 return print_size(ctx);
400 case MODE_GETECCSTART:
401 case MODE_GETVERITYSTART:
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000402 return get_start(mode, inp_filenames.front());
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100403 case MODE_ENCODE:
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000404 return encode(ctx, inp_filenames, fec_filename);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100405 case MODE_DECODE:
Sami Tolvanenf35a15d2015-12-18 15:28:06 +0000406 return decode(ctx, inp_filenames, fec_filename, out_filename);
Sami Tolvanen29bf7372015-05-16 15:14:14 +0100407 default:
408 abort();
409 }
410
411 return 1;
412}