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 <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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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