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