Extract files from a YAFFS2 file system image http://bernhard-ehlers.de/projects/unyaffs.html

unyaffs.c 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. /*
  2. * unyaffs: extract files from yaffs2 file system image to current directory
  3. *
  4. * Created by Kai Wei <kai.wei.cn@gmail.com>
  5. * Modified by Bernhard Ehlers <be@bernhard-ehlers.de>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. */
  11. /*
  12. * History:
  13. * V0.1 2008-12-29
  14. * Initial version uploaded to http://code.google.com/p/unyaffs/
  15. * V0.8 2011-08-25
  16. * Fork created at https://github.com/ehlers/unyaffs
  17. * Support of chunksizes from 2k to 16k
  18. * Restore special files (device nodes)
  19. * Restore file date and time
  20. * Restore file ownership, when run as root
  21. * File listing
  22. * Much more error checking
  23. * V0.9 2011-09-03
  24. * Allow - as filename for stdin
  25. * Optional base dir for file extraction
  26. * V0.9.1 2012-04-12
  27. * Additional flash layout: 8k chunk size, 448 byte spare
  28. * V0.9.2 2012-04-18
  29. * Additional flash layout: 8k chunk size, 368 byte spare
  30. * New options -c and -s to set chunk and spare size, obsoletes option -l
  31. * Add support for bad block information in spare header.
  32. * Allow first data chunk to be stored in image before file header.
  33. * V0.9.3 2012-04-30
  34. * check result of lchown system call
  35. * Code cleanup
  36. * V0.9.4 2012-05-01
  37. * No predefined flash layouts, detect all possible layouts
  38. * Option -d shows detected flash layout, no extraction
  39. * V0.9.5 2012-12-02
  40. * Bug in verifying the -s (spare) parameter
  41. * V0.9.6 2013-04-09
  42. * Added man page
  43. * V0.9.7 2018-03-29
  44. * Directory creation doesn't fail, if it already exists
  45. * Fix compiler warnings for newer GCC
  46. */
  47. #define VERSION "0.9.7"
  48. /* check if lutimes is available */
  49. #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || (defined(__APPLE__) && defined(__MACH__))
  50. #define HAS_LUTIMES 1
  51. #endif
  52. #include <stdio.h>
  53. #include <unistd.h>
  54. #include <stdlib.h>
  55. #include <stddef.h>
  56. #include <stdarg.h>
  57. #include <string.h>
  58. #include <time.h>
  59. #include <errno.h>
  60. #include <fcntl.h>
  61. #include <sys/stat.h>
  62. #include <sys/types.h>
  63. #if defined(__linux__) || defined(__GLIBC__)
  64. #include <sys/sysmacros.h>
  65. #endif
  66. #ifdef HAS_LUTIMES
  67. #include <sys/time.h>
  68. #else
  69. #include <utime.h>
  70. #endif
  71. #include "unyaffs.h"
  72. #define MIN_CHUNK_SIZE 2048
  73. #define MAX_CHUNK_SIZE 16384
  74. #define MIN_SPARE_SIZE 64
  75. #define MAX_SPARE_SIZE 512
  76. #define HASH_SIZE 7001
  77. #define MAX_WARN 20
  78. #define YAFFS_OBJECTID_ROOT 1
  79. #define STD_PERMS (S_IRWXU|S_IRWXG|S_IRWXO)
  80. #define EXTRA_PERMS (S_ISUID|S_ISGID|S_ISVTX)
  81. unsigned char data[MAX_CHUNK_SIZE + MAX_SPARE_SIZE];
  82. unsigned char buffer[4*(MAX_CHUNK_SIZE + MAX_SPARE_SIZE)];
  83. unsigned char *chunk_data = data;
  84. unsigned char *spare_data = NULL;
  85. int chunk_size = MIN_CHUNK_SIZE;
  86. int spare_size = MIN_SPARE_SIZE;
  87. int spare_off = 0;
  88. int buf_len = 0;
  89. int buf_idx = 0;
  90. int chunk_no = 0;
  91. int warn_count = 0;
  92. int warn_chown = 0;
  93. int img_file;
  94. int opt_list;
  95. int opt_verbose;
  96. typedef struct _object {
  97. unsigned id;
  98. struct _object *next;
  99. yaffs_ObjectType type;
  100. unsigned prev_dir_id;
  101. __u32 atime;
  102. __u32 mtime;
  103. char path_name[1]; /* variable length, must be last */
  104. } object;
  105. object *obj_list[HASH_SIZE];
  106. unsigned last_dir_id;
  107. int set_utime(const char *filename, __u32 yst_atime, __u32 yst_mtime) {
  108. #ifdef HAS_LUTIMES
  109. struct timeval ftime[2];
  110. ftime[0].tv_sec = yst_atime;
  111. ftime[0].tv_usec = 0;
  112. ftime[1].tv_sec = yst_mtime;
  113. ftime[1].tv_usec = 0;
  114. return lutimes(filename, ftime);
  115. #else
  116. struct utimbuf ftime;
  117. ftime.actime = yst_atime;
  118. ftime.modtime = yst_mtime;
  119. return utime(filename, &ftime);
  120. #endif
  121. }
  122. /* error reporting function, similar to GNU error() */
  123. static void prt_err(int status, int errnum, const char *format, ...) {
  124. va_list varg;
  125. va_start(varg, format);
  126. fflush(stdout);
  127. vfprintf(stderr, format, varg);
  128. if (errnum != 0)
  129. fprintf(stderr, ": %s", strerror(errnum));
  130. fprintf(stderr, "\n");
  131. va_end(varg);
  132. if (status != 0)
  133. exit(status);
  134. }
  135. /* read function, which handles partial and interrupted reads */
  136. ssize_t safe_read(int fd, void *buf, size_t len) {
  137. char *ptr = buf;
  138. ssize_t offset, ret;
  139. offset = 0;
  140. while (offset < len) {
  141. ret = read(fd, ptr+offset, len-offset);
  142. if (ret < 0) {
  143. if (errno != EAGAIN && errno != EINTR)
  144. return -1;
  145. } else if (ret == 0)
  146. break;
  147. else
  148. offset += ret;
  149. }
  150. return offset;
  151. }
  152. /* write function, which handles partial and interrupted writes */
  153. ssize_t safe_write(int fd, void *buf, size_t len) {
  154. char *ptr = buf;
  155. ssize_t offset, ret;
  156. offset = 0;
  157. while (offset < len) {
  158. ret = write(fd, ptr+offset, len-offset);
  159. if (ret < 0) {
  160. if (errno != EAGAIN && errno != EINTR)
  161. return -1;
  162. } else if (ret == 0)
  163. break;
  164. else
  165. offset += ret;
  166. }
  167. return offset;
  168. }
  169. /*
  170. * mkdirpath - creates directories including intermediate dirs
  171. */
  172. static int mkdirpath(const char *name, mode_t mode) {
  173. struct stat st;
  174. char *cp;
  175. char *buf;
  176. if ((buf = malloc(strlen(name)+1)) == NULL)
  177. { errno = ENOMEM; return -1; }
  178. strcpy(buf, name);
  179. cp = buf;
  180. while ((cp = strchr(cp, '/')) != NULL) {
  181. *cp = '\0';
  182. mkdir(buf, 0755);
  183. *cp++ = '/';
  184. }
  185. free(buf);
  186. if (mkdir(name, mode) < 0) {
  187. if (stat(name, &st) < 0 || !S_ISDIR(st.st_mode))
  188. return -1;
  189. chmod(name, mode);
  190. }
  191. return 0;
  192. }
  193. /*
  194. * save_lchown - call lchown and check result
  195. */
  196. static void safe_lchown(const char *path, uid_t owner, gid_t group) {
  197. if (lchown(path, owner, group) < 0) {
  198. if (errno == EPERM || errno == EINVAL)
  199. warn_chown = 1;
  200. else
  201. prt_err(1, errno, "Can't chown %s", path);
  202. }
  203. }
  204. static void init_obj_list(void) {
  205. object *obj;
  206. unsigned idx;
  207. for (idx = 0; idx < HASH_SIZE; idx++)
  208. obj_list[idx] = NULL;
  209. last_dir_id = 0;
  210. obj = malloc(offsetof(object, path_name) + 2);
  211. if (obj == NULL)
  212. prt_err(1, 0, "Malloc struct object failed.");
  213. obj->id = YAFFS_OBJECTID_ROOT;
  214. obj->type = YAFFS_OBJECT_TYPE_DIRECTORY;
  215. obj->prev_dir_id = 0;
  216. obj->atime = obj->mtime = 0;
  217. strcpy(obj->path_name, ".");
  218. idx = obj->id % HASH_SIZE;
  219. obj->next = obj_list[idx];
  220. obj_list[idx] = obj;
  221. }
  222. static object *get_object(unsigned id) {
  223. object *obj;
  224. obj = obj_list[id % HASH_SIZE];
  225. while (obj != NULL && obj->id != id)
  226. obj = obj->next;
  227. return obj;
  228. }
  229. static object *add_object(yaffs_ObjectHeader *oh, yaffs_PackedTags2 *pt) {
  230. object *obj, *parent;
  231. unsigned idx;
  232. obj = get_object(pt->t.objectId);
  233. if (pt->t.objectId == YAFFS_OBJECTID_ROOT) {
  234. if (obj == NULL)
  235. prt_err(1, 0, "Missing root object");
  236. if (oh->type != YAFFS_OBJECT_TYPE_DIRECTORY)
  237. prt_err(1, 0, "Root object must be directory");
  238. if (last_dir_id == 0)
  239. last_dir_id = YAFFS_OBJECTID_ROOT;
  240. } else {
  241. if (oh->type != YAFFS_OBJECT_TYPE_FILE &&
  242. oh->type != YAFFS_OBJECT_TYPE_DIRECTORY &&
  243. oh->type != YAFFS_OBJECT_TYPE_SYMLINK &&
  244. oh->type != YAFFS_OBJECT_TYPE_HARDLINK &&
  245. oh->type != YAFFS_OBJECT_TYPE_SPECIAL &&
  246. oh->type != YAFFS_OBJECT_TYPE_UNKNOWN)
  247. prt_err(1, 0, "Illegal type %d in object %u (%s)",
  248. oh->type, pt->t.objectId, oh->name);
  249. if (oh->name[0] == '\0' || strchr(oh->name, '/') != NULL ||
  250. strcmp(oh->name, ".") == 0 || strcmp(oh->name, "..") == 0)
  251. prt_err(1, 0, "Illegal file name %s in object %u",
  252. oh->name, pt->t.objectId);
  253. if (obj != NULL)
  254. prt_err(1, 0, "Duplicate objectId %u", pt->t.objectId);
  255. parent = get_object(oh->parentObjectId);
  256. if (parent == NULL)
  257. prt_err(1, 0, "Invalid parentObjectId %u in object %u (%s)",
  258. oh->parentObjectId, pt->t.objectId, oh->name);
  259. if (parent->type != YAFFS_OBJECT_TYPE_DIRECTORY)
  260. prt_err(1, ENOTDIR, "File %s can't be created in %s",
  261. oh->name, parent->path_name);
  262. obj = malloc(offsetof(object, path_name) +
  263. strlen(parent->path_name) + strlen(oh->name) + 2);
  264. if (obj == NULL)
  265. prt_err(1, 0, "Malloc struct object failed.");
  266. obj->id = pt->t.objectId;
  267. obj->type = oh->type;
  268. if (obj->type == YAFFS_OBJECT_TYPE_DIRECTORY) {
  269. obj->prev_dir_id = last_dir_id;
  270. last_dir_id = obj->id;
  271. } else
  272. obj->prev_dir_id = 0;
  273. if (strcmp(parent->path_name, ".") == 0) {
  274. strcpy(obj->path_name, oh->name);
  275. } else {
  276. strcpy(obj->path_name, parent->path_name);
  277. strcat(obj->path_name, "/");
  278. strcat(obj->path_name, oh->name);
  279. }
  280. idx = obj->id % HASH_SIZE;
  281. obj->next = obj_list[idx];
  282. obj_list[idx] = obj;
  283. }
  284. obj->atime = oh->yst_atime;
  285. obj->mtime = oh->yst_mtime;
  286. return obj;
  287. }
  288. void set_dirs_utime(void) {
  289. unsigned id;
  290. object *obj;
  291. id = last_dir_id;
  292. while (id != 0 && (obj = get_object(id)) != NULL) {
  293. set_utime(obj->path_name, obj->atime, obj->mtime);
  294. id = obj->prev_dir_id;
  295. }
  296. }
  297. static void prt_node(char *name, yaffs_ObjectHeader *oh) {
  298. object *eq_obj;
  299. struct tm tm;
  300. time_t mtime;
  301. mode_t mode;
  302. char type;
  303. char fsize[16];
  304. char perm[10];
  305. /* get file type, size, mtine and mode */
  306. eq_obj = NULL;
  307. strcpy(fsize, "0");
  308. mtime = oh->yst_mtime;
  309. mode = oh->yst_mode;
  310. switch(oh->type) {
  311. case YAFFS_OBJECT_TYPE_FILE: type = '-';
  312. snprintf(fsize, sizeof(fsize), "%d", oh->fileSize);
  313. break;
  314. case YAFFS_OBJECT_TYPE_DIRECTORY: type = 'd'; break;
  315. case YAFFS_OBJECT_TYPE_SYMLINK: type = 'l'; break;
  316. case YAFFS_OBJECT_TYPE_HARDLINK: type = 'h';
  317. eq_obj = get_object(oh->equivalentObjectId);
  318. mtime = eq_obj != NULL ? eq_obj->mtime : 0;
  319. mode = STD_PERMS;
  320. break;
  321. case YAFFS_OBJECT_TYPE_SPECIAL:
  322. switch (oh->yst_mode & S_IFMT) {
  323. case S_IFBLK: type = 'b';
  324. snprintf(fsize, sizeof(fsize), "%d,%4d",
  325. major(oh->yst_rdev),
  326. minor(oh->yst_rdev));
  327. break;
  328. case S_IFCHR: type = 'c';
  329. snprintf(fsize, sizeof(fsize), "%d,%4d",
  330. major(oh->yst_rdev),
  331. minor(oh->yst_rdev));
  332. break;
  333. case S_IFIFO: type = 'p'; break;
  334. case S_IFSOCK: type = 's'; break;
  335. default: type = '?'; break;
  336. }
  337. break;
  338. default: type = '?'; break;
  339. }
  340. /* get file permissions */
  341. perm[0] = mode & S_IRUSR ? 'r' : '-';
  342. perm[1] = mode & S_IWUSR ? 'w' : '-';
  343. perm[2] = mode & S_IXUSR ? 'x' : '-';
  344. perm[3] = mode & S_IRGRP ? 'r' : '-';
  345. perm[4] = mode & S_IWGRP ? 'w' : '-';
  346. perm[5] = mode & S_IXGRP ? 'x' : '-';
  347. perm[6] = mode & S_IROTH ? 'r' : '-';
  348. perm[7] = mode & S_IWOTH ? 'w' : '-';
  349. perm[8] = mode & S_IXOTH ? 'x' : '-';
  350. if (mode & S_ISUID) perm[2] = perm[2] == '-' ? 'S' : 's';
  351. if (mode & S_ISGID) perm[5] = perm[5] == '-' ? 'S' : 's';
  352. if (mode & S_ISVTX) perm[8] = perm[8] == '-' ? 'T' : 't';
  353. perm[9] = '\0';
  354. /* print file infos */
  355. localtime_r(&mtime, &tm);
  356. printf("%c%s %8s %4d-%02d-%02d %02d:%02d %s",
  357. type, perm, fsize,
  358. tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  359. tm.tm_hour, tm.tm_min, name);
  360. /* link destination */
  361. if (oh->type == YAFFS_OBJECT_TYPE_HARDLINK) {
  362. if (eq_obj == NULL)
  363. printf(" -> !!! Invalid !!!");
  364. else
  365. printf(" -> /%s", eq_obj->path_name);
  366. } else if (oh->type == YAFFS_OBJECT_TYPE_SYMLINK) {
  367. printf(" -> %s", oh->alias);
  368. }
  369. printf("\n");
  370. }
  371. int read_chunk(void);
  372. static struct {
  373. unsigned objectId;
  374. int chunk_no;
  375. unsigned char data[MAX_CHUNK_SIZE + MAX_SPARE_SIZE];
  376. } saved_chunk;
  377. int next_data_chunk(void) {
  378. yaffs_PackedTags2 *pt;
  379. unsigned next_objectId;
  380. unsigned next_chunkId;
  381. int ret = 0;
  382. pt = (yaffs_PackedTags2 *)spare_data;
  383. next_objectId = pt->t.objectId;
  384. next_chunkId = pt->t.chunkId + 1;
  385. if (saved_chunk.objectId == next_objectId && /* use saved chunk ? */
  386. next_chunkId == 1) {
  387. memcpy(chunk_data, saved_chunk.data, chunk_size);
  388. memcpy(spare_data, saved_chunk.data+chunk_size, spare_size);
  389. saved_chunk.objectId = 0;
  390. ret = 1;
  391. } else {
  392. if (read_chunk() && /* valid next chunk ? */
  393. pt->t.objectId == next_objectId &&
  394. pt->t.chunkId == next_chunkId) {
  395. ret = 1;
  396. }
  397. }
  398. return ret;
  399. }
  400. void process_chunk(void) {
  401. yaffs_ObjectHeader oh;
  402. yaffs_PackedTags2 *pt;
  403. object *obj, *eq_obj;
  404. int out_file, remain, s;
  405. oh = *(yaffs_ObjectHeader *)chunk_data;
  406. pt = (yaffs_PackedTags2 *)spare_data;
  407. if (pt->t.sequenceNumber == 0xffffffff) /* empty object */
  408. return;
  409. if (saved_chunk.objectId != 0 && /* saved chunk is not part of object */
  410. saved_chunk.objectId != pt->t.objectId) {
  411. prt_err(0, 0, "Warning: Invalid header at chunk #%d, skipping...",
  412. saved_chunk.chunk_no);
  413. if (++warn_count >= MAX_WARN)
  414. prt_err(1, 0, "Giving up");
  415. saved_chunk.objectId = 0;
  416. }
  417. if (pt->t.chunkId == 1) { /* save chunk #1 */
  418. saved_chunk.objectId = pt->t.objectId;
  419. saved_chunk.chunk_no = chunk_no;
  420. memcpy(saved_chunk.data, chunk_data, chunk_size);
  421. memcpy(saved_chunk.data+chunk_size, spare_data, spare_size);
  422. return;
  423. } else if (pt->t.chunkId != 0) { /* not a new object */
  424. prt_err(0, 0, "Warning: Invalid header at chunk #%d, skipping...",
  425. chunk_no);
  426. if (++warn_count >= MAX_WARN)
  427. prt_err(1, 0, "Giving up");
  428. return;
  429. }
  430. obj = add_object(&oh, pt);
  431. /* listing */
  432. if (opt_verbose)
  433. prt_node(obj->path_name, &oh);
  434. else if (opt_list)
  435. printf("%s\n", obj->path_name);
  436. if (opt_list) {
  437. if (oh.type == YAFFS_OBJECT_TYPE_FILE) {
  438. remain = oh.fileSize; /* skip over data chunks */
  439. while(remain > 0) {
  440. if (!next_data_chunk())
  441. prt_err(1, 0, "Broken image file");
  442. remain -= pt->t.byteCount;
  443. }
  444. }
  445. return;
  446. }
  447. switch(oh.type) {
  448. case YAFFS_OBJECT_TYPE_FILE:
  449. remain = oh.fileSize;
  450. out_file = creat(obj->path_name, oh.yst_mode & STD_PERMS);
  451. if (out_file < 0)
  452. prt_err(1, errno, "Can't create file %s", obj->path_name);
  453. while(remain > 0) {
  454. if (!next_data_chunk())
  455. prt_err(1, 0, "Broken image file");
  456. s = (remain < pt->t.byteCount) ? remain : pt->t.byteCount;
  457. if (safe_write(out_file, chunk_data, s) < 0)
  458. prt_err(1, errno, "Can't write to %s", obj->path_name);
  459. remain -= s;
  460. }
  461. close(out_file);
  462. safe_lchown(obj->path_name, oh.yst_uid, oh.yst_gid);
  463. if ((oh.yst_mode & EXTRA_PERMS) != 0 &&
  464. chmod(obj->path_name, oh.yst_mode) < 0)
  465. prt_err(0, errno, "Warning: Can't chmod %s", obj->path_name);
  466. break;
  467. case YAFFS_OBJECT_TYPE_SYMLINK:
  468. if (symlink(oh.alias, obj->path_name) < 0)
  469. prt_err(1, errno, "Can't create symlink %s", obj->path_name);
  470. safe_lchown(obj->path_name, oh.yst_uid, oh.yst_gid);
  471. break;
  472. case YAFFS_OBJECT_TYPE_DIRECTORY:
  473. if (pt->t.objectId != YAFFS_OBJECTID_ROOT &&
  474. mkdirpath(obj->path_name, oh.yst_mode & STD_PERMS) < 0)
  475. prt_err(1, errno, "Can't create directory %s", obj->path_name);
  476. safe_lchown(obj->path_name, oh.yst_uid, oh.yst_gid);
  477. if ((pt->t.objectId == YAFFS_OBJECTID_ROOT ||
  478. (oh.yst_mode & EXTRA_PERMS) != 0) &&
  479. chmod(obj->path_name, oh.yst_mode) < 0)
  480. prt_err(0, errno, "Warning: Can't chmod %s", obj->path_name);
  481. break;
  482. case YAFFS_OBJECT_TYPE_HARDLINK:
  483. eq_obj = get_object(oh.equivalentObjectId);
  484. if (eq_obj == NULL)
  485. prt_err(1, 0, "Invalid equivalentObjectId %u in object %u (%s)",
  486. oh.equivalentObjectId, pt->t.objectId, oh.name);
  487. if (link(eq_obj->path_name, obj->path_name) < 0)
  488. prt_err(1, errno, "Can't create hardlink %s", obj->path_name);
  489. break;
  490. case YAFFS_OBJECT_TYPE_SPECIAL:
  491. if (mknod(obj->path_name, oh.yst_mode, oh.yst_rdev) < 0) {
  492. if (errno == EPERM || errno == EINVAL)
  493. prt_err(0, errno, "Warning: Can't create device %s", obj->path_name);
  494. else
  495. prt_err(1, errno, "Can't create device %s", obj->path_name);
  496. } else
  497. safe_lchown(obj->path_name, oh.yst_uid, oh.yst_gid);
  498. break;
  499. case YAFFS_OBJECT_TYPE_UNKNOWN:
  500. break;
  501. }
  502. /* set file date and time */
  503. switch(oh.type) {
  504. case YAFFS_OBJECT_TYPE_FILE:
  505. case YAFFS_OBJECT_TYPE_SPECIAL:
  506. #ifdef HAS_LUTIMES
  507. case YAFFS_OBJECT_TYPE_SYMLINK:
  508. #endif
  509. set_utime(obj->path_name,
  510. oh.yst_atime, oh.yst_mtime);
  511. break;
  512. case YAFFS_OBJECT_TYPE_DIRECTORY:
  513. default:
  514. break;
  515. }
  516. }
  517. int read_chunk(void) {
  518. ssize_t s, len, offset;
  519. chunk_no++;
  520. len = chunk_size + spare_size;
  521. offset = 0;
  522. memset(data, 0xff, len);
  523. if (buf_len > buf_idx) { /* copy from buffer */
  524. s = buf_len - buf_idx;
  525. if (s > len) s = len;
  526. memcpy(data, buffer+buf_idx, s);
  527. buf_idx += s; offset += s;
  528. }
  529. if (offset < len) { /* read from file */
  530. s = safe_read(img_file, data+offset, len-offset);
  531. if (s < 0)
  532. prt_err(1, errno, "Read image file");
  533. offset += s;
  534. }
  535. if (offset != 0 && offset != len) /* partial chunk */
  536. prt_err(1, 0, "Broken image file");
  537. if (offset == len && spare_off != 0) { /* bad block info */
  538. memmove(data+chunk_size, data+chunk_size+spare_off,
  539. spare_size-spare_off);
  540. memset(data+len-spare_off, 0xff, spare_off);
  541. }
  542. return offset != 0;
  543. }
  544. int check_layout(int chunk, int spare, int off) {
  545. yaffs_ObjectHeader oh;
  546. yaffs_PackedTags2 pt;
  547. int i, detect;
  548. for (i = 0, detect = 1; i < 4 && detect; i++) {
  549. memcpy(&oh, buffer + i * (chunk+spare), sizeof(oh));
  550. memcpy(&pt, buffer + i * (chunk+spare) + chunk+off, sizeof(pt));
  551. detect =
  552. (pt.t.chunkId > 0 && pt.t.chunkId <= 10 &&
  553. pt.t.objectId >= 0x100 && pt.t.objectId <= (0x100+10) &&
  554. pt.t.byteCount <= chunk) ||
  555. (pt.t.chunkId == 0 &&
  556. (pt.t.objectId == YAFFS_OBJECTID_ROOT ||
  557. (pt.t.objectId >= 0x100 && pt.t.objectId <= (0x100+10))) &&
  558. pt.t.byteCount == 0xffff &&
  559. (oh.type == YAFFS_OBJECT_TYPE_FILE ||
  560. oh.type == YAFFS_OBJECT_TYPE_DIRECTORY ||
  561. oh.type == YAFFS_OBJECT_TYPE_SYMLINK ||
  562. oh.type == YAFFS_OBJECT_TYPE_HARDLINK ||
  563. oh.type == YAFFS_OBJECT_TYPE_SPECIAL));
  564. }
  565. return detect;
  566. }
  567. void detect_flash_layout(int show, int first) {
  568. int cnt;
  569. int chunk, spare, off;
  570. memset(buffer, 0xff, sizeof(buffer));
  571. buf_len = safe_read(img_file, buffer, sizeof(buffer));
  572. if (buf_len < 0)
  573. prt_err(1, errno, "Read image file");
  574. if (show)
  575. printf("Detected flash layout(s):\n");
  576. cnt = 0;
  577. for (chunk = MIN_CHUNK_SIZE; chunk <= MAX_CHUNK_SIZE; chunk *= 2) {
  578. for (spare = MIN_SPARE_SIZE; spare <= MAX_SPARE_SIZE; spare += 16) {
  579. for (off = 0; off <= 2; off += 2) {
  580. if (check_layout(chunk, spare, off)) {
  581. cnt++;
  582. if (show) {
  583. printf("%2s -c %-2d -s %-3d : chunk size = %2dK, spare size = %3d, %sbad block info\n",
  584. off ? "-b" : "", chunk / 1024, spare,
  585. chunk / 1024, spare, off ? "" : "no ");
  586. }
  587. if (first) {
  588. chunk_size = chunk;
  589. spare_size = spare;
  590. spare_off = off;
  591. return;
  592. }
  593. }
  594. }
  595. }
  596. }
  597. if (cnt == 0) {
  598. if (show) {
  599. printf("-- none --\n");
  600. exit(1);
  601. } else {
  602. prt_err(1, 0, "Can't determine flash layout, perhaps not a yaffs2 image");
  603. }
  604. }
  605. }
  606. void usage(void) {
  607. fprintf(stderr, "\
  608. unyaffs V%s - extract files from a YAFFS2 file system image.\n\
  609. \n\
  610. Usage: unyaffs [options] <image_file_name> [<extract_directory>]\n\
  611. \n\
  612. Options:\n\
  613. -d detection of flash layout, no extraction\n\
  614. -b spare contains bad block information\n\
  615. -c <chunk size> set chunk size in KByte (default: autodetect, max: %d)\n\
  616. -s <spare size> set spare size in Byte (default: autodetect, max: %d)\n\
  617. -t list image contents\n\
  618. -v verbose output\n\
  619. -V print version\n\
  620. ", VERSION, MAX_CHUNK_SIZE / 1024, MAX_SPARE_SIZE);
  621. exit(1);
  622. }
  623. int main(int argc, char **argv) {
  624. int ch;
  625. char *ep;
  626. int opt_detect;
  627. int opt_bad;
  628. int opt_chunk;
  629. int opt_spare;
  630. /* handle command line options */
  631. opt_detect = 0;
  632. opt_bad = 0;
  633. opt_chunk = 0;
  634. opt_spare = 0;
  635. opt_list = 0;
  636. opt_verbose = 0;
  637. while ((ch = getopt(argc, argv, "dbc:s:tvVh?")) > 0) {
  638. switch (ch) {
  639. case 'd':
  640. opt_detect = 1;
  641. break;
  642. case 'b':
  643. opt_bad = 1;
  644. break;
  645. case 'c':
  646. opt_chunk = strtol(optarg, &ep, 0);
  647. if (*ep != '\0' ||
  648. opt_chunk < 0 ||
  649. opt_chunk > (MAX_CHUNK_SIZE / 1024) )
  650. usage();
  651. break;
  652. case 's':
  653. opt_spare = strtol(optarg, &ep, 0);
  654. if (*ep != '\0' ||
  655. opt_spare < 0 ||
  656. opt_spare > MAX_SPARE_SIZE)
  657. usage();
  658. break;
  659. case 't':
  660. opt_list = 1;
  661. break;
  662. case 'v':
  663. opt_verbose = 1;
  664. break;
  665. case 'V':
  666. printf("V%s\n", VERSION);
  667. exit(0);
  668. break;
  669. case 'h':
  670. case '?':
  671. default:
  672. usage();
  673. break;
  674. }
  675. }
  676. /* extract rest of command line parameters */
  677. if ((argc - optind) < 1 || (argc - optind) > 2)
  678. usage();
  679. if (strcmp(argv[optind], "-") == 0) { /* image file from stdin ? */
  680. img_file = 0;
  681. } else {
  682. img_file = open(argv[optind], O_RDONLY);
  683. if (img_file < 0)
  684. prt_err(1, errno, "Open image file failed");
  685. }
  686. if (opt_detect) {
  687. detect_flash_layout(1, 0);
  688. return 0;
  689. }
  690. if (opt_chunk == 0 || opt_spare == 0) {
  691. detect_flash_layout(0, 1);
  692. if (opt_verbose)
  693. prt_err(0, 0,
  694. "Header check OK, chunk size = %dK, spare size = %d, %sbad block info.",
  695. chunk_size/1024, spare_size, spare_off ? "" : "no ");
  696. } else {
  697. chunk_size = opt_chunk * 1024;
  698. spare_size = opt_spare;
  699. spare_off = opt_bad ? 2 : 0;
  700. }
  701. spare_data = data + chunk_size;
  702. if ((argc - optind) == 2 && !opt_list) {
  703. if (mkdirpath(argv[optind+1], 0755) < 0)
  704. prt_err(1, errno, "Can't mkdir %s", argv[optind+1]);
  705. if (chdir(argv[optind+1]) < 0)
  706. prt_err(1, errno, "Can't chdir to %s", argv[optind+1]);
  707. }
  708. umask(0);
  709. init_obj_list();
  710. saved_chunk.objectId = 0;
  711. while (read_chunk()) {
  712. process_chunk();
  713. }
  714. set_dirs_utime();
  715. close(img_file);
  716. if (warn_chown)
  717. #ifdef __CYGWIN__
  718. prt_err(0, 0, "Warning: Can't restore owner/group attribute (limitation of Cygwin/Windows)");
  719. #else
  720. prt_err(0, 0, "Warning: Can't restore owner/group attribute, run unyaffs as root");
  721. #endif
  722. return 0;
  723. }