text: /*
   * Copyright 2014 The Chromium OS Authors. All rights reserved.
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  #include <errno.h>
  #include <fcntl.h>
  #include <getopt.h>
  #include <inttypes.h>
  #include <limits.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #include "fmap.h"
  #include "futility.h"
  
  
  static const char usage[] = "\n"
  	"Usage:  " MYNAME " %s [OPTIONS] FILE AREA:file [AREA:file ...]\n"
  	"\n"
  	"Replace the contents of specific FMAP areas. This is the complement\n"
  	"of " MYNAME " dump_fmap -x FILE AREA [AREA ...]\n"
  	"\n"
  	"Options:\n"
  	"  -o OUTFILE     Write the result to this file, instead of modifying\n"
  	"                   the input file. This is safer, since there are no\n"
  	"                   safeguards against doing something stupid.\n"
  	"\n"
  	"Example:\n"
  	"\n"
  	"  This will clear the RO_VPD area, and scramble VBLOCK_B:\n"
  	"\n"
  	"  " MYNAME " %s bios.bin RO_VPD:/dev/zero VBLOCK_B:/dev/urandom\n"
  	"\n";
  
  static void help_and_quit(const char *prog)
  {
  	printf(usage, prog, prog);
  }
  
  static const struct option long_opts[] = {
  	/* name    hasarg *flag  val */
  	{NULL,          0, NULL, 0},
  };
  static char *short_opts = ":o:";
  
  
  static int copy_to_area(char *file, uint8_t *buf, uint32_t len, char *area)
  {
  	FILE *fp;
  	int retval = 0;
  	int n;
  
  	fp = fopen(file, "r");
  	if (!fp) {
  		fprintf(stderr, "area %s: can't open %s for reading: %s\n",
  			area, file, strerror(errno));
  		return 1;
  	}
  
  	n = fread(buf, 1, len, fp);
  	if (n == 0) {
  		if (feof(fp))
  			fprintf(stderr, "area %s: unexpected EOF on %s\n",
  				area, file);
  		if (ferror(fp))
  			fprintf(stderr, "area %s: can't read from %s: %s\n",
  				area, file, strerror(errno));
  		retval = 1;
  	} else if (n < len) {
  		fprintf(stderr, "Warning on area %s: only read %d "
  			"(not %d) from %s\n", area, n, len, file);
  	}
  
  	if (0 != fclose(fp)) {
  		fprintf(stderr, "area %s: error closing %s: %s\n",
  			area, file, strerror(errno));
  		retval = 1;
  	}
  
  	return retval;
  }
  
  
  static int do_load_fmap(int argc, char *argv[])
  {
  	char *infile = 0;
  	char *outfile = 0;
  	uint8_t *buf;
  	uint32_t len;
  	FmapHeader *fmap;
  	FmapAreaHeader *ah;
  	int errorcnt = 0;
  	int fd, i;
  
  	opterr = 0;		/* quiet, you */
  	while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
  		switch (i) {
  		case 'o':
  			outfile = optarg;
  			break;
  		case '?':
  			if (optopt)
  				fprintf(stderr, "Unrecognized option: -%c\n",
  					optopt);
  			else
  				fprintf(stderr, "Unrecognized option\n");
  			errorcnt++;
  			break;
  		case ':':
  			fprintf(stderr, "Missing argument to -%c\n", optopt);
  			errorcnt++;
  			break;
  		default:
  			DIE;
  		}
  	}
  
  	if (errorcnt) {
  		help_and_quit(argv[0]);
  		return 1;
  	}
  
  	if (argc - optind < 2) {
  		fprintf(stderr,
  			"You must specify an input file"
  			" and at least one AREA:file argument\n");
  		help_and_quit(argv[0]);
  		return 1;
  	}
  
  	infile = argv[optind++];
  
  	/* okay, let's do it ... */
  	if (outfile)
  		futil_copy_file_or_die(infile, outfile);
  	else
  		outfile = infile;
  
  	fd = open(outfile, O_RDWR);
  	if (fd < 0) {
  		fprintf(stderr, "Can't open %s: %s\n",
  			outfile, strerror(errno));
  		return 1;
  	}
  
  	errorcnt |= futil_map_file(fd, MAP_RW, &buf, &len);
  	if (errorcnt)
  		goto done_file;
  
  	fmap = fmap_find(buf, len);
  	if (!fmap) {
  		fprintf(stderr, "Can't find an FMAP in %s\n", infile);
  		errorcnt++;
  		goto done_map;
  	}
  
  	for (i = optind; i < argc; i++) {
  		char *a = argv[i];
  		char *f = strchr(a, ':');
  
  		if (!f || a == f || *(f+1) == '\0') {
  			fprintf(stderr, "argument \"%s\" is bogus\n", a);
  			errorcnt++;
  			break;
  		}
  		*f++ = '\0';
  		uint8_t *area_buf = fmap_find_by_name(buf, len, fmap, a, &ah);
  		if (!area_buf) {
  			fprintf(stderr, "Can't find area \"%s\" in FMAP\n", a);
  			errorcnt++;
  			break;
  		}
  
  		if (0 != copy_to_area(f, area_buf, ah->area_size, a)) {
  			errorcnt++;
  			break;
  		}
  	}
  
  done_map:
  	errorcnt |= futil_unmap_file(fd, 1, buf, len);
  
  done_file:
  
  	if (0 != close(fd)) {
  		fprintf(stderr, "Error closing %s: %s\n",
  			outfile, strerror(errno));
  		errorcnt++;
  	}
  
  	return !!errorcnt;
  }
  
  DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap,
  		      VBOOT_VERSION_ALL,
  		      "Replace the contents of specified FMAP areas",
  		      help_and_quit);
  