/* * 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. */ #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 <mtd_swab.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; uint16_t *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++) { uint16_t 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; uint8_t 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(uint16_t) + 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(uint16_t); 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; }