summaryrefslogtreecommitdiff
path: root/rfddump.c
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@shinybook.infradead.org>2006-04-11 19:09:16 -0400
committerDavid Woodhouse <dwmw2@shinybook.infradead.org>2006-04-11 19:09:16 -0400
commit72212c137c574b564723327af751c4054c7cfca6 (patch)
tree13be2da9d4be695002d6ea9c396660c986e5fa5c /rfddump.c
Initial commit
Diffstat (limited to 'rfddump.c')
-rw-r--r--rfddump.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/rfddump.c b/rfddump.c
new file mode 100644
index 0000000..268e610
--- /dev/null
+++ b/rfddump.c
@@ -0,0 +1,338 @@
+/*
+ * rfddump.c
+ *
+ * Copyright (C) 2005 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: rfddump.c,v 1.3 2005/11/07 11:15:13 gleixner Exp $
+ */
+
+#define _XOPEN_SOURCE 500 /* For pread */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* next is an array of mapping for each corresponding sector */
+#define RFD_MAGIC 0x9193
+#define HEADER_MAP_OFFSET 3
+#define SECTOR_DELETED 0x0000
+#define SECTOR_ZERO 0xfffe
+#define SECTOR_FREE 0xffff
+
+#define SECTOR_SIZE 512
+
+#define SECTORS_PER_TRACK 63
+
+
+struct rfd {
+ int block_size;
+ int block_count;
+ int header_sectors;
+ int data_sectors;
+ int header_size;
+ __u16 *header;
+ int sector_count;
+ int *sector_map;
+ const char *mtd_filename;
+ const char *out_filename;
+ int verbose;
+};
+
+#define PROGRAM "rfddump"
+#define VERSION "$Revision 1.0 $"
+
+void display_help(void)
+{
+ printf("Usage: " PROGRAM " [OPTIONS] MTD-device filename\n"
+ "Dumps the contents of a resident flash disk\n"
+ "\n"
+ "-h --help display this help and exit\n"
+ "-V --version output version information and exit\n"
+ "-v --verbose Be verbose\n"
+ "-b size --blocksize Block size (defaults to erase unit)\n");
+ exit(0);
+}
+
+void display_version(void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+
+ exit(0);
+}
+
+void process_options(int argc, char *argv[], struct rfd *rfd)
+{
+ int error = 0;
+
+ rfd->block_size = 0;
+ rfd->verbose = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hvVb:";
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V', },
+ { "blocksize", required_argument, 0, 'b' },
+ { "verbose", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 'h':
+ display_help();
+ break;
+ case 'V':
+ display_version();
+ break;
+ case 'v':
+ rfd->verbose = 1;
+ break;
+ case 'b':
+ rfd->block_size = atoi(optarg);
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 2 || error)
+ display_help();
+
+ rfd->mtd_filename = argv[optind];
+ rfd->out_filename = argv[optind + 1];
+}
+
+int build_block_map(struct rfd *rfd, int fd, int block)
+{
+ int i;
+ int sectors;
+
+ if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size)
+ != rfd->header_size) {
+ return -1;
+ }
+
+ if (__le16_to_cpu(rfd->header[0]) != RFD_MAGIC) {
+ if (rfd->verbose)
+ printf("Block #%02d: Magic missing\n", block);
+
+ return 0;
+ }
+
+ sectors = 0;
+ for (i=0; i<rfd->data_sectors; i++) {
+ __u16 entry = __le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]);
+
+ if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
+ continue;
+
+ if (entry == SECTOR_ZERO)
+ entry = 0;
+
+ if (entry >= rfd->sector_count) {
+ fprintf(stderr, "%s: warning: sector %d out of range\n",
+ rfd->mtd_filename, entry);
+ continue;
+ }
+
+ if (rfd->sector_map[entry] != -1) {
+ fprintf(stderr, "%s: warning: more than one entry "
+ "for sector %d\n", rfd->mtd_filename, entry);
+ continue;
+ }
+
+ rfd->sector_map[entry] = rfd->block_size * block +
+ (i + rfd->header_sectors) * SECTOR_SIZE;
+ sectors++;
+ }
+
+ if (rfd->verbose)
+ printf("Block #%02d: %d sectors\n", block, sectors);
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, sectors_per_block;
+ mtd_info_t mtd_info;
+ struct rfd rfd;
+ int i, blocks_found;
+ int out_fd = 0;
+ __u8 sector[512];
+ int blank, rc, cylinders;
+
+ process_options(argc, argv, &rfd);
+
+ fd = open(rfd.mtd_filename, O_RDONLY);
+ if (fd == -1) {
+ perror(rfd.mtd_filename);
+ return 1;
+ }
+
+ if (rfd.block_size == 0) {
+ if (ioctl(fd, MEMGETINFO, &mtd_info)) {
+ perror(rfd.mtd_filename);
+ close(fd);
+ return 1;
+ }
+
+ if (mtd_info.type != MTD_NORFLASH) {
+ fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename);
+ close(fd);
+ return 2;
+ }
+
+ sectors_per_block = mtd_info.erasesize / SECTOR_SIZE;
+
+ rfd.block_size = mtd_info.erasesize;
+ rfd.block_count = mtd_info.size / mtd_info.erasesize;
+ } else {
+ struct stat st;
+
+ if (fstat(fd, &st) == -1) {
+ perror(rfd.mtd_filename);
+ close(fd);
+ return 1;
+ }
+
+ if (st.st_size % SECTOR_SIZE)
+ fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename);
+
+ sectors_per_block = rfd.block_size / SECTOR_SIZE;
+
+ if (st.st_size % rfd.block_size)
+ fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename);
+
+ rfd.block_count = st.st_size / rfd.block_size;
+
+ if (!rfd.block_count) {
+ fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename);
+ close(fd);
+ return 2;
+ }
+ }
+
+ rfd.header_sectors =
+ ((HEADER_MAP_OFFSET + sectors_per_block) *
+ sizeof(__u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ rfd.data_sectors = sectors_per_block - rfd.header_sectors;
+ cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1)
+ / SECTORS_PER_TRACK;
+ rfd.sector_count = cylinders * SECTORS_PER_TRACK;
+ rfd.header_size =
+ (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(__u16);
+
+ rfd.header = malloc(rfd.header_size);
+ if (!rfd.header) {
+ perror(PROGRAM);
+ close(fd);
+ return 2;
+ }
+ rfd.sector_map = malloc(rfd.sector_count * sizeof(int));
+ if (!rfd.sector_map) {
+ perror(PROGRAM);
+ close(fd);
+ free(rfd.sector_map);
+ return 2;
+ }
+
+ rfd.mtd_filename = rfd.mtd_filename;
+
+ for (i=0; i<rfd.sector_count; i++)
+ rfd.sector_map[i] = -1;
+
+ for (blocks_found=i=0; i<rfd.block_count; i++) {
+ rc = build_block_map(&rfd, fd, i);
+ if (rc > 0)
+ blocks_found++;
+ if (rc < 0)
+ goto err;
+ }
+
+ if (!blocks_found) {
+ fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] != -1)
+ break;
+ }
+
+ if (i == rfd.sector_count) {
+ fprintf(stderr, "%s: no sectors found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ out_fd = open(rfd.out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (out_fd == -1) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+
+ blank = 0;
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] == -1) {
+ memset(sector, 0, SECTOR_SIZE);
+ blank++;
+ } else {
+ if (pread(fd, sector, SECTOR_SIZE, rfd.sector_map[i])
+ != SECTOR_SIZE) {
+ perror(rfd.mtd_filename);
+ goto err;
+ }
+ }
+
+ if (write(out_fd, sector, SECTOR_SIZE) != SECTOR_SIZE) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+ }
+
+ if (rfd.verbose)
+ printf("Copied %d sectors (%d blank)\n", rfd.sector_count, blank);
+
+ close(out_fd);
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 0;
+
+err:
+ if (out_fd)
+ close(out_fd);
+
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 2;
+}
+