rfddump.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * rfddump.c
  3. *
  4. * Copyright (C) 2005 Sean Young <sean@mess.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. */
  11. #define PROGRAM_NAME "rfddump"
  12. #define _XOPEN_SOURCE 500 /* For pread */
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <sys/ioctl.h>
  18. #include <string.h>
  19. #include <fcntl.h>
  20. #include <unistd.h>
  21. #include <getopt.h>
  22. #include <mtd/mtd-user.h>
  23. #include <linux/types.h>
  24. #include <mtd_swab.h>
  25. #include "common.h"
  26. /* next is an array of mapping for each corresponding sector */
  27. #define RFD_MAGIC 0x9193
  28. #define HEADER_MAP_OFFSET 3
  29. #define SECTOR_DELETED 0x0000
  30. #define SECTOR_ZERO 0xfffe
  31. #define SECTOR_FREE 0xffff
  32. #define SECTOR_SIZE 512
  33. #define SECTORS_PER_TRACK 63
  34. struct rfd {
  35. int block_size;
  36. int block_count;
  37. int header_sectors;
  38. int data_sectors;
  39. int header_size;
  40. uint16_t *header;
  41. int sector_count;
  42. int *sector_map;
  43. const char *mtd_filename;
  44. const char *out_filename;
  45. int verbose;
  46. };
  47. static void display_help(int status)
  48. {
  49. printf("Usage: %s [OPTIONS] MTD-device filename\n"
  50. "Dumps the contents of a resident flash disk\n"
  51. "\n"
  52. "-h --help display this help and exit\n"
  53. "-V --version output version information and exit\n"
  54. "-v --verbose Be verbose\n"
  55. "-b size --blocksize Block size (defaults to erase unit)\n",
  56. PROGRAM_NAME);
  57. exit(status);
  58. }
  59. static void display_version(void)
  60. {
  61. common_print_version();
  62. printf("This is free software; see the source for copying conditions. There is NO\n"
  63. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
  64. exit(0);
  65. }
  66. static void process_options(int argc, char *argv[], struct rfd *rfd)
  67. {
  68. int error = 0;
  69. rfd->block_size = 0;
  70. rfd->verbose = 0;
  71. for (;;) {
  72. int option_index = 0;
  73. static const char *short_options = "hvVb:";
  74. static const struct option long_options[] = {
  75. { "help", no_argument, 0, 'h' },
  76. { "version", no_argument, 0, 'V', },
  77. { "blocksize", required_argument, 0, 'b' },
  78. { "verbose", no_argument, 0, 'v' },
  79. { NULL, 0, 0, 0 }
  80. };
  81. int c = getopt_long(argc, argv, short_options,
  82. long_options, &option_index);
  83. if (c == EOF)
  84. break;
  85. switch (c) {
  86. case 'h':
  87. display_help(EXIT_SUCCESS);
  88. break;
  89. case 'V':
  90. display_version();
  91. break;
  92. case 'v':
  93. rfd->verbose = 1;
  94. break;
  95. case 'b':
  96. rfd->block_size = atoi(optarg);
  97. break;
  98. case '?':
  99. error = 1;
  100. break;
  101. }
  102. }
  103. if ((argc - optind) != 2 || error)
  104. display_help(EXIT_FAILURE);
  105. rfd->mtd_filename = argv[optind];
  106. rfd->out_filename = argv[optind + 1];
  107. }
  108. static int build_block_map(struct rfd *rfd, int fd, int block)
  109. {
  110. int i;
  111. int sectors;
  112. if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size)
  113. != rfd->header_size) {
  114. return -1;
  115. }
  116. if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) {
  117. if (rfd->verbose)
  118. printf("Block #%02d: Magic missing\n", block);
  119. return 0;
  120. }
  121. sectors = 0;
  122. for (i=0; i<rfd->data_sectors; i++) {
  123. uint16_t entry = le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]);
  124. if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
  125. continue;
  126. if (entry == SECTOR_ZERO)
  127. entry = 0;
  128. if (entry >= rfd->sector_count) {
  129. fprintf(stderr, "%s: warning: sector %d out of range\n",
  130. rfd->mtd_filename, entry);
  131. continue;
  132. }
  133. if (rfd->sector_map[entry] != -1) {
  134. fprintf(stderr, "%s: warning: more than one entry "
  135. "for sector %d\n", rfd->mtd_filename, entry);
  136. continue;
  137. }
  138. rfd->sector_map[entry] = rfd->block_size * block +
  139. (i + rfd->header_sectors) * SECTOR_SIZE;
  140. sectors++;
  141. }
  142. if (rfd->verbose)
  143. printf("Block #%02d: %d sectors\n", block, sectors);
  144. return 1;
  145. }
  146. int main(int argc, char *argv[])
  147. {
  148. int fd, sectors_per_block;
  149. mtd_info_t mtd_info;
  150. struct rfd rfd;
  151. int i, blocks_found;
  152. int out_fd = 0;
  153. uint8_t sector[512];
  154. int blank, rc, cylinders;
  155. process_options(argc, argv, &rfd);
  156. fd = open(rfd.mtd_filename, O_RDONLY);
  157. if (fd == -1) {
  158. perror(rfd.mtd_filename);
  159. return 1;
  160. }
  161. if (rfd.block_size == 0) {
  162. if (ioctl(fd, MEMGETINFO, &mtd_info)) {
  163. perror(rfd.mtd_filename);
  164. close(fd);
  165. return 1;
  166. }
  167. if (mtd_info.type != MTD_NORFLASH) {
  168. fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename);
  169. close(fd);
  170. return 2;
  171. }
  172. sectors_per_block = mtd_info.erasesize / SECTOR_SIZE;
  173. rfd.block_size = mtd_info.erasesize;
  174. rfd.block_count = mtd_info.size / mtd_info.erasesize;
  175. } else {
  176. struct stat st;
  177. if (fstat(fd, &st) == -1) {
  178. perror(rfd.mtd_filename);
  179. close(fd);
  180. return 1;
  181. }
  182. if (st.st_size % SECTOR_SIZE)
  183. fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename);
  184. sectors_per_block = rfd.block_size / SECTOR_SIZE;
  185. if (st.st_size % rfd.block_size)
  186. fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename);
  187. rfd.block_count = st.st_size / rfd.block_size;
  188. if (!rfd.block_count) {
  189. fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename);
  190. close(fd);
  191. return 2;
  192. }
  193. }
  194. rfd.header_sectors =
  195. ((HEADER_MAP_OFFSET + sectors_per_block) *
  196. sizeof(uint16_t) + SECTOR_SIZE - 1) / SECTOR_SIZE;
  197. rfd.data_sectors = sectors_per_block - rfd.header_sectors;
  198. cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1)
  199. / SECTORS_PER_TRACK;
  200. rfd.sector_count = cylinders * SECTORS_PER_TRACK;
  201. rfd.header_size =
  202. (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(uint16_t);
  203. rfd.header = malloc(rfd.header_size);
  204. if (!rfd.header) {
  205. perror(PROGRAM_NAME);
  206. close(fd);
  207. return 2;
  208. }
  209. rfd.sector_map = malloc(rfd.sector_count * sizeof(int));
  210. if (!rfd.sector_map) {
  211. perror(PROGRAM_NAME);
  212. close(fd);
  213. free(rfd.sector_map);
  214. return 2;
  215. }
  216. rfd.mtd_filename = rfd.mtd_filename;
  217. for (i=0; i<rfd.sector_count; i++)
  218. rfd.sector_map[i] = -1;
  219. for (blocks_found=i=0; i<rfd.block_count; i++) {
  220. rc = build_block_map(&rfd, fd, i);
  221. if (rc > 0)
  222. blocks_found++;
  223. if (rc < 0)
  224. goto err;
  225. }
  226. if (!blocks_found) {
  227. fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename);
  228. goto err;
  229. }
  230. for (i=0; i<rfd.sector_count; i++) {
  231. if (rfd.sector_map[i] != -1)
  232. break;
  233. }
  234. if (i == rfd.sector_count) {
  235. fprintf(stderr, "%s: no sectors found\n", rfd.mtd_filename);
  236. goto err;
  237. }
  238. out_fd = open(rfd.out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  239. if (out_fd == -1) {
  240. perror(rfd.out_filename);
  241. goto err;
  242. }
  243. blank = 0;
  244. for (i=0; i<rfd.sector_count; i++) {
  245. if (rfd.sector_map[i] == -1) {
  246. memset(sector, 0, SECTOR_SIZE);
  247. blank++;
  248. } else {
  249. if (pread(fd, sector, SECTOR_SIZE, rfd.sector_map[i])
  250. != SECTOR_SIZE) {
  251. perror(rfd.mtd_filename);
  252. goto err;
  253. }
  254. }
  255. if (write(out_fd, sector, SECTOR_SIZE) != SECTOR_SIZE) {
  256. perror(rfd.out_filename);
  257. goto err;
  258. }
  259. }
  260. if (rfd.verbose)
  261. printf("Copied %d sectors (%d blank)\n", rfd.sector_count, blank);
  262. close(out_fd);
  263. close(fd);
  264. free(rfd.header);
  265. free(rfd.sector_map);
  266. return 0;
  267. err:
  268. if (out_fd > 0)
  269. close(out_fd);
  270. close(fd);
  271. free(rfd.header);
  272. free(rfd.sector_map);
  273. return 2;
  274. }