2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
32 #include <android-base/file.h>
43 static void encode_rs(struct image_proc_ctx *ctx)
45 struct image *fcx = ctx->ctx;
47 uint8_t data[fcx->rs_n];
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);
55 encode_rs_char(ctx->rs, data, &fcx->fec[ctx->fec_pos]);
56 ctx->fec_pos += fcx->roots;
60 static void decode_rs(struct image_proc_ctx *ctx)
62 struct image *fcx = ctx->ctx;
64 uint8_t data[fcx->rs_n + fcx->roots];
67 assert(sizeof(data) == FEC_RSM);
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);
74 memcpy(&data[fcx->rs_n], &fcx->fec[ctx->fec_pos], fcx->roots);
75 rv = decode_rs_char(ctx->rs, data, NULL, 0);
78 FATAL("failed to recover [%" PRIu64 ", %" PRIu64 ")\n",
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]);
89 ctx->fec_pos += fcx->roots;
95 printf("fec: a tool for encoding and decoding files using RS(255, N).\n"
97 "usage: fec <mode> [ <options> ] [ <data> <fec> [ <output> ] ]\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"
105 " -h show this help\n"
106 " -v enable verbose logging\n"
107 " -r, --roots=<bytes> number of parity bytes\n"
108 " -j, --threads=<threads> number of threads to use\n"
109 " -S treat data as a sparse file\n"
110 "decoding options:\n"
111 " -i, --inplace correct <data> in place\n"
117 static uint64_t parse_arg(const char *arg, const char *name, uint64_t maxval)
122 unsigned long long int value = strtoull(arg, &endptr, 0);
124 if (arg[0] == '\0' || *endptr != '\0' ||
125 (errno == ERANGE && value == ULLONG_MAX)) {
126 FATAL("invalid value of %s\n", name);
128 if (value > maxval) {
129 FATAL("value of roots too large (max. %" PRIu64 ")\n", maxval);
132 return (uint64_t)value;
135 static int print_size(image& ctx)
137 /* output size including header */
138 printf("%" PRIu64 "\n", fec_ecc_get_size(ctx.inp_size, ctx.roots));
142 static int get_start(int mode, const std::string& filename)
144 fec::io fh(filename, O_RDONLY, FEC_VERITY_DISABLE);
147 FATAL("failed to open input\n");
150 if (mode == MODE_GETECCSTART) {
151 fec_ecc_metadata data;
153 if (!fh.get_ecc_metadata(data)) {
154 FATAL("no ecc data\n");
157 printf("%" PRIu64 "\n", data.start);
159 fec_verity_metadata data;
161 if (!fh.get_verity_metadata(data)) {
162 FATAL("no verity data\n");
165 printf("%" PRIu64 "\n", data.data_size);
171 static int encode(image& ctx, const std::vector<std::string>& inp_filenames,
172 const std::string& fec_filename)
175 FATAL("invalid parameters: inplace can only used when decoding\n");
178 if (!image_load(inp_filenames, &ctx)) {
179 FATAL("failed to read input\n");
182 if (!image_ecc_new(fec_filename, &ctx)) {
183 FATAL("failed to allocate ecc\n");
186 INFO("encoding RS(255, %d) to '%s' for input files:\n", ctx.rs_n,
187 fec_filename.c_str());
191 for (const auto& fn : inp_filenames) {
192 INFO("\t%zu: '%s'\n", n++, fn.c_str());
196 INFO("\traw fec size: %u\n", ctx.fec_size);
197 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
198 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
201 if (!image_process(encode_rs, &ctx)) {
202 FATAL("failed to process input\n");
205 if (!image_ecc_save(&ctx)) {
206 FATAL("failed to write output\n");
213 static int decode(image& ctx, const std::vector<std::string>& inp_filenames,
214 const std::string& fec_filename, std::string& out_filename)
216 const std::string& inp_filename = inp_filenames.front();
218 if (ctx.inplace && ctx.sparse) {
219 FATAL("invalid parameters: inplace cannot be used with sparse "
223 if (!image_ecc_load(fec_filename, &ctx) ||
224 !image_load(inp_filenames, &ctx)) {
225 FATAL("failed to read input\n");
229 INFO("correcting '%s' using RS(255, %d) from '%s'\n",
230 inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
232 out_filename = inp_filename;
234 INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
235 inp_filename.c_str(),
236 out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
237 fec_filename.c_str());
241 INFO("\traw fec size: %u\n", ctx.fec_size);
242 INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
243 INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
246 if (!image_process(decode_rs, &ctx)) {
247 FATAL("failed to process input\n");
251 INFO("corrected %" PRIu64 " errors\n", ctx.rv);
253 INFO("no errors found\n");
256 if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
257 FATAL("failed to write output\n");
264 int main(int argc, char **argv)
266 std::string fec_filename;
267 std::string out_filename;
268 std::vector<std::string> inp_filenames;
269 int mode = MODE_ENCODE;
273 ctx.roots = FEC_DEFAULT_ROOTS;
276 const static struct option long_options[] = {
277 {"help", no_argument, 0, 'h'},
278 {"encode", no_argument, 0, 'e'},
279 {"decode", no_argument, 0, 'd'},
280 {"sparse", no_argument, 0, 'S'},
281 {"roots", required_argument, 0, 'r'},
282 {"inplace", no_argument, 0, 'i'},
283 {"threads", required_argument, 0, 'j'},
284 {"print-fec-size", required_argument, 0, 's'},
285 {"get-ecc-start", required_argument, 0, 'E'},
286 {"get-verity-start", required_argument, 0, 'V'},
287 {"verbose", no_argument, 0, 'v'},
290 int c = getopt_long(argc, argv, "hedSr:imj:s:E:V:v", long_options, NULL);
302 if (mode != MODE_ENCODE) {
307 if (mode != MODE_ENCODE) {
313 ctx.roots = (int)parse_arg(optarg, "roots", FEC_RSM);
319 ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
322 if (mode != MODE_ENCODE) {
325 mode = MODE_PRINTSIZE;
326 ctx.inp_size = parse_arg(optarg, "print-fec-size", UINT64_MAX);
329 if (mode != MODE_ENCODE) {
332 mode = MODE_GETECCSTART;
333 inp_filenames.push_back(optarg);
336 if (mode != MODE_ENCODE) {
339 mode = MODE_GETVERITYSTART;
340 inp_filenames.push_back(optarg);
355 assert(ctx.roots > 0 && ctx.roots < FEC_RSM);
357 /* check for input / output parameters */
358 if (mode == MODE_ENCODE) {
359 /* allow multiple input files */
360 for (int i = 0; i < (argc - 1); ++i) {
361 inp_filenames.push_back(argv[i]);
364 if (inp_filenames.empty()) {
368 /* the last one is the output file */
369 fec_filename = argv[argc - 1];
370 } else if (mode == MODE_DECODE) {
371 if (argc < 2 || argc > 3) {
373 } else if (argc == 3) {
377 out_filename = argv[2];
380 inp_filenames.push_back(argv[0]);
381 fec_filename = argv[1];
386 return print_size(ctx);
387 case MODE_GETECCSTART:
388 case MODE_GETVERITYSTART:
389 return get_start(mode, inp_filenames.front());
391 return encode(ctx, inp_filenames, fec_filename);
393 return decode(ctx, inp_filenames, fec_filename, out_filename);