• 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 <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <android-base/file.h>
33 #include "image.h"
34 
35 enum {
36     MODE_ENCODE,
37     MODE_DECODE,
38     MODE_PRINTSIZE,
39     MODE_GETECCSTART,
40     MODE_GETVERITYSTART
41 };
42 
encode_rs(struct image_proc_ctx * ctx)43 static 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 
decode_rs(struct image_proc_ctx * ctx)60 static 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 
usage()93 static 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"
108            "  -j, --threads=<threads>           number of threads to use\n"
109            "  -S                                treat data as a sparse file\n"
110            "encoding options:\n"
111            "  -p, --padding=<bytes>             add padding after ECC data\n"
112            "decoding options:\n"
113            "  -i, --inplace                     correct <data> in place\n"
114         );
115 
116     return 1;
117 }
118 
parse_arg(const char * arg,const char * name,uint64_t maxval)119 static 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 
print_size(image & ctx)137 static 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 
get_start(int mode,const std::string & filename)144 static int get_start(int mode, const std::string& filename)
145 {
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 
159         printf("%" PRIu64 "\n", data.start);
160     } 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 
encode(image & ctx,const std::vector<std::string> & inp_filenames,const std::string & fec_filename)173 static int encode(image& ctx, const std::vector<std::string>& inp_filenames,
174         const std::string& fec_filename)
175 {
176     if (ctx.inplace) {
177         FATAL("invalid parameters: inplace can only used when decoding\n");
178     }
179 
180     if (!image_load(inp_filenames, &ctx)) {
181         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 
188     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 
193     for (const auto& fn : inp_filenames) {
194         INFO("\t%zu: '%s'\n", n++, fn.c_str());
195     }
196 
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 
decode(image & ctx,const std::vector<std::string> & inp_filenames,const std::string & fec_filename,std::string & out_filename)215 static int decode(image& ctx, const std::vector<std::string>& inp_filenames,
216         const std::string& fec_filename, std::string& out_filename)
217 {
218     const std::string& inp_filename = inp_filenames.front();
219 
220     if (ctx.inplace && ctx.sparse) {
221         FATAL("invalid parameters: inplace cannot be used with sparse "
222             "files\n");
223     }
224 
225     if (ctx.padding) {
226         FATAL("invalid parameters: padding is only relevant when encoding\n");
227     }
228 
229     if (!image_ecc_load(fec_filename, &ctx) ||
230             !image_load(inp_filenames, &ctx)) {
231         FATAL("failed to read input\n");
232     }
233 
234     if (ctx.inplace) {
235         INFO("correcting '%s' using RS(255, %d) from '%s'\n",
236             inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
237 
238         out_filename = inp_filename;
239     } else {
240         INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
241             inp_filename.c_str(),
242             out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
243             fec_filename.c_str());
244     }
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 
262     if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
263         FATAL("failed to write output\n");
264     }
265 
266     image_free(&ctx);
267     return 0;
268 }
269 
main(int argc,char ** argv)270 int main(int argc, char **argv)
271 {
272     std::string fec_filename;
273     std::string out_filename;
274     std::vector<std::string> inp_filenames;
275     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'},
289             {"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'},
293             {"padding", required_argument, 0, 'p'},
294             {"verbose", no_argument, 0, 'v'},
295             {NULL, 0, 0, 0}
296         };
297         int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, NULL);
298         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;
325         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;
340             inp_filenames.push_back(optarg);
341             break;
342         case 'V':
343             if (mode != MODE_ENCODE) {
344                 return usage();
345             }
346             mode = MODE_GETVERITYSTART;
347             inp_filenames.push_back(optarg);
348             break;
349         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;
355         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 */
371     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) {
384         if (argc < 2 || argc > 3) {
385             return usage();
386         } else if (argc == 3) {
387             if (ctx.inplace) {
388                 return usage();
389             }
390             out_filename = argv[2];
391         }
392 
393         inp_filenames.push_back(argv[0]);
394         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:
402         return get_start(mode, inp_filenames.front());
403     case MODE_ENCODE:
404         return encode(ctx, inp_filenames, fec_filename);
405     case MODE_DECODE:
406         return decode(ctx, inp_filenames, fec_filename, out_filename);
407     default:
408         abort();
409     }
410 
411     return 1;
412 }
413