jffs2.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * jffs2 on-disk structure generator for mtd
  3. *
  4. * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License v2
  8. * as published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. * Based on:
  15. * JFFS2 -- Journalling Flash File System, Version 2.
  16. * Copyright © 2001-2007 Red Hat, Inc.
  17. * Created by David Woodhouse <dwmw2@infradead.org>
  18. */
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <stdint.h>
  22. #include <stdio.h>
  23. #include <fcntl.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <dirent.h>
  27. #include <unistd.h>
  28. #include <endian.h>
  29. #include "jffs2.h"
  30. #include "crc32.h"
  31. #include "mtd.h"
  32. #define PAD(x) (((x)+3)&~3)
  33. #if BYTE_ORDER == BIG_ENDIAN
  34. # define CLEANMARKER "\x19\x85\x20\x03\x00\x00\x00\x0c\xf0\x60\xdc\x98"
  35. #else
  36. # define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
  37. #endif
  38. static int last_ino = 0;
  39. static int last_version = 0;
  40. static char *buf = NULL;
  41. static int ofs = 0;
  42. static int outfd = -1;
  43. static int mtdofs = 0;
  44. static int target_ino = 0;
  45. static void prep_eraseblock(void);
  46. static void pad(int size)
  47. {
  48. if ((ofs % size == 0) && (ofs < erasesize))
  49. return;
  50. if (ofs < erasesize) {
  51. memset(buf + ofs, 0xff, (size - (ofs % size)));
  52. ofs += (size - (ofs % size));
  53. }
  54. ofs = ofs % erasesize;
  55. if (ofs == 0) {
  56. while (mtd_block_is_bad(outfd, mtdofs) && (mtdofs < mtdsize)) {
  57. if (!quiet)
  58. fprintf(stderr, "\nSkipping bad block at 0x%08x ", mtdofs);
  59. mtdofs += erasesize;
  60. /* Move the file pointer along over the bad block. */
  61. lseek(outfd, erasesize, SEEK_CUR);
  62. }
  63. mtd_erase_block(outfd, mtdofs);
  64. write(outfd, buf, erasesize);
  65. mtdofs += erasesize;
  66. }
  67. }
  68. static inline int rbytes(void)
  69. {
  70. return erasesize - (ofs % erasesize);
  71. }
  72. static inline void add_data(char *ptr, int len)
  73. {
  74. if (ofs + len > erasesize) {
  75. pad(erasesize);
  76. prep_eraseblock();
  77. }
  78. memcpy(buf + ofs, ptr, len);
  79. ofs += len;
  80. }
  81. static void prep_eraseblock(void)
  82. {
  83. if (ofs > 0)
  84. return;
  85. add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
  86. }
  87. static int add_dirent(const char *name, const char type, int parent)
  88. {
  89. struct jffs2_raw_dirent *de;
  90. if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
  91. pad(erasesize);
  92. prep_eraseblock();
  93. last_ino++;
  94. memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
  95. de = (struct jffs2_raw_dirent *) (buf + ofs);
  96. de->magic = JFFS2_MAGIC_BITMASK;
  97. de->nodetype = JFFS2_NODETYPE_DIRENT;
  98. de->type = type;
  99. de->name_crc = crc32(0, name, strlen(name));
  100. de->ino = last_ino++;
  101. de->pino = parent;
  102. de->totlen = sizeof(*de) + strlen(name);
  103. de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
  104. de->version = last_version++;
  105. de->mctime = 0;
  106. de->nsize = strlen(name);
  107. de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
  108. memcpy(de->name, name, strlen(name));
  109. ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
  110. pad(4);
  111. return de->ino;
  112. }
  113. static int add_dir(const char *name, int parent)
  114. {
  115. struct jffs2_raw_inode ri;
  116. int inode;
  117. inode = add_dirent(name, IFTODT(S_IFDIR), parent);
  118. if (rbytes() < sizeof(ri))
  119. pad(erasesize);
  120. prep_eraseblock();
  121. memset(&ri, 0, sizeof(ri));
  122. ri.magic = JFFS2_MAGIC_BITMASK;
  123. ri.nodetype = JFFS2_NODETYPE_INODE;
  124. ri.totlen = sizeof(ri);
  125. ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
  126. ri.ino = inode;
  127. ri.mode = S_IFDIR | 0755;
  128. ri.uid = ri.gid = 0;
  129. ri.atime = ri.ctime = ri.mtime = 0;
  130. ri.isize = ri.csize = ri.dsize = 0;
  131. ri.version = 1;
  132. ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
  133. ri.data_crc = 0;
  134. add_data((char *) &ri, sizeof(ri));
  135. pad(4);
  136. return inode;
  137. }
  138. static void add_file(const char *name, int parent)
  139. {
  140. int inode, f_offset = 0, fd;
  141. struct jffs2_raw_inode ri;
  142. struct stat st;
  143. char wbuf[4096];
  144. const char *fname;
  145. if (stat(name, &st)) {
  146. fprintf(stderr, "File %s does not exist\n", name);
  147. return;
  148. }
  149. fname = strrchr(name, '/');
  150. if (fname)
  151. fname++;
  152. else
  153. fname = name;
  154. inode = add_dirent(fname, IFTODT(S_IFREG), parent);
  155. memset(&ri, 0, sizeof(ri));
  156. ri.magic = JFFS2_MAGIC_BITMASK;
  157. ri.nodetype = JFFS2_NODETYPE_INODE;
  158. ri.ino = inode;
  159. ri.mode = st.st_mode;
  160. ri.uid = ri.gid = 0;
  161. ri.atime = st.st_atime;
  162. ri.ctime = st.st_ctime;
  163. ri.mtime = st.st_mtime;
  164. ri.isize = st.st_size;
  165. ri.compr = 0;
  166. ri.usercompr = 0;
  167. fd = open(name, 0);
  168. if (fd < 0) {
  169. fprintf(stderr, "File %s does not exist\n", name);
  170. return;
  171. }
  172. for (;;) {
  173. int len = 0;
  174. for (;;) {
  175. len = rbytes() - sizeof(ri);
  176. if (len > 128)
  177. break;
  178. pad(erasesize);
  179. prep_eraseblock();
  180. }
  181. if (len > sizeof(wbuf))
  182. len = sizeof(wbuf);
  183. len = read(fd, wbuf, len);
  184. if (len <= 0)
  185. break;
  186. ri.totlen = sizeof(ri) + len;
  187. ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
  188. ri.version = ++last_version;
  189. ri.offset = f_offset;
  190. ri.csize = ri.dsize = len;
  191. ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
  192. ri.data_crc = crc32(0, wbuf, len);
  193. f_offset += len;
  194. add_data((char *) &ri, sizeof(ri));
  195. add_data(wbuf, len);
  196. pad(4);
  197. prep_eraseblock();
  198. }
  199. close(fd);
  200. }
  201. int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename)
  202. {
  203. outfd = fd;
  204. mtdofs = ofs;
  205. buf = malloc(erasesize);
  206. target_ino = 1;
  207. if (!last_ino)
  208. last_ino = 1;
  209. add_file(filename, target_ino);
  210. pad(erasesize);
  211. /* add eof marker, pad to eraseblock size and write the data */
  212. add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
  213. pad(erasesize);
  214. free(buf);
  215. return (mtdofs - ofs);
  216. }
  217. void mtd_parse_jffs2data(const char *buf, const char *dir)
  218. {
  219. struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
  220. unsigned int ofs = 0;
  221. while (ofs < erasesize) {
  222. node = (struct jffs2_unknown_node *) (buf + ofs);
  223. if (node->magic != 0x1985)
  224. break;
  225. ofs += PAD(node->totlen);
  226. if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
  227. struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
  228. /* is this the right directory name and is it a subdirectory of / */
  229. if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize))
  230. target_ino = de->ino;
  231. /* store the last inode and version numbers for adding extra files */
  232. if (last_ino < de->ino)
  233. last_ino = de->ino;
  234. if (last_version < de->version)
  235. last_version = de->version;
  236. }
  237. }
  238. }
  239. int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
  240. {
  241. int err = -1, fdeof = 0;
  242. outfd = mtd_check_open(mtd);
  243. if (outfd < 0)
  244. return -1;
  245. if (quiet < 2)
  246. fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
  247. buf = malloc(erasesize);
  248. if (!buf) {
  249. fprintf(stderr, "Out of memory!\n");
  250. goto done;
  251. }
  252. if (!*dir)
  253. target_ino = 1;
  254. /* parse the structure of the jffs2 first
  255. * locate the directory that the file is going to be placed in */
  256. for(;;) {
  257. struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
  258. if (read(outfd, buf, erasesize) != erasesize) {
  259. fdeof = 1;
  260. break;
  261. }
  262. mtdofs += erasesize;
  263. if (node->magic == 0x8519) {
  264. fprintf(stderr, "Error: wrong endianness filesystem\n");
  265. goto done;
  266. }
  267. /* assume no magic == end of filesystem
  268. * the filesystem will probably end with be32(0xdeadc0de) */
  269. if (node->magic != 0x1985)
  270. break;
  271. mtd_parse_jffs2data(buf, dir);
  272. }
  273. if (fdeof) {
  274. fprintf(stderr, "Error: No room for additional data\n");
  275. goto done;
  276. }
  277. /* jump back one eraseblock */
  278. mtdofs -= erasesize;
  279. lseek(outfd, mtdofs, SEEK_SET);
  280. ofs = 0;
  281. if (!last_ino)
  282. last_ino = 1;
  283. if (!target_ino)
  284. target_ino = add_dir(dir, 1);
  285. add_file(filename, target_ino);
  286. pad(erasesize);
  287. /* add eof marker, pad to eraseblock size and write the data */
  288. add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
  289. pad(erasesize);
  290. err = 0;
  291. if (trx_fixup) {
  292. trx_fixup(outfd, mtd);
  293. }
  294. done:
  295. close(outfd);
  296. if (buf)
  297. free(buf);
  298. return err;
  299. }