• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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