Home | History | Annotate | Download | only in fec
      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 
     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 
     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, NULL, 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 
     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 
    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 
    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 
    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 
    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 
    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 
    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, 0, 'h'},
    283             {"encode", no_argument, 0, 'e'},
    284             {"decode", no_argument, 0, 'd'},
    285             {"sparse", no_argument, 0, 'S'},
    286             {"roots", required_argument, 0, 'r'},
    287             {"inplace", no_argument, 0, 'i'},
    288             {"threads", required_argument, 0, 'j'},
    289             {"print-fec-size", required_argument, 0, 's'},
    290             {"get-ecc-start", required_argument, 0, 'E'},
    291             {"get-verity-start", required_argument, 0, 'V'},
    292             {"padding", required_argument, 0, 'p'},
    293             {"verbose", no_argument, 0, 'v'},
    294             {NULL, 0, 0, 0}
    295         };
    296         int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, NULL);
    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