mtd: fix trx_fixup
[openwrt/svn-archive/archive.git] / package / mtd / src / jffs2.c
1 /*
2 * jffs2 on-disk structure generator for mtd
3 *
4 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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
33 #define PAD(x) (((x)+3)&~3)
34
35 #if BYTE_ORDER == BIG_ENDIAN
36 # define CLEANMARKER "\x19\x85\x20\x03\x00\x00\x00\x0c\xf0\x60\xdc\x98"
37 #else
38 # define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
39 #endif
40
41 static int last_ino = 0;
42 static int last_version = 0;
43 static char *buf = NULL;
44 static int ofs = 0;
45 static int outfd = -1;
46 static int mtdofs = 0;
47 static int target_ino = 0;
48
49 static void prep_eraseblock(void);
50
51 static void pad(int size)
52 {
53 if ((ofs % size == 0) && (ofs < erasesize))
54 return;
55
56 if (ofs < erasesize) {
57 memset(buf + ofs, 0xff, (size - (ofs % size)));
58 ofs += (size - (ofs % size));
59 }
60 ofs = ofs % erasesize;
61 if (ofs == 0) {
62 mtd_erase_block(outfd, mtdofs);
63 write(outfd, buf, erasesize);
64 mtdofs += erasesize;
65 }
66 }
67
68 static inline int rbytes(void)
69 {
70 return erasesize - (ofs % erasesize);
71 }
72
73 static inline void add_data(char *ptr, int len)
74 {
75 if (ofs + len > erasesize) {
76 pad(erasesize);
77 prep_eraseblock();
78 }
79 memcpy(buf + ofs, ptr, len);
80 ofs += len;
81 }
82
83 static void prep_eraseblock(void)
84 {
85 if (ofs > 0)
86 return;
87
88 add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
89 }
90
91 static int add_dirent(const char *name, const char type, int parent)
92 {
93 struct jffs2_raw_dirent *de;
94
95 if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
96 pad(erasesize);
97
98 prep_eraseblock();
99 last_ino++;
100 memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
101 de = (struct jffs2_raw_dirent *) (buf + ofs);
102
103 de->magic = JFFS2_MAGIC_BITMASK;
104 de->nodetype = JFFS2_NODETYPE_DIRENT;
105 de->type = type;
106 de->name_crc = crc32(0, name, strlen(name));
107 de->ino = last_ino++;
108 de->pino = parent;
109 de->totlen = sizeof(*de) + strlen(name);
110 de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
111 de->version = last_version++;
112 de->mctime = 0;
113 de->nsize = strlen(name);
114 de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
115 memcpy(de->name, name, strlen(name));
116
117 ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
118 pad(4);
119
120 return de->ino;
121 }
122
123 static int add_dir(const char *name, int parent)
124 {
125 struct jffs2_raw_inode ri;
126 int inode;
127
128 inode = add_dirent(name, IFTODT(S_IFDIR), parent);
129
130 if (rbytes() < sizeof(ri))
131 pad(erasesize);
132 prep_eraseblock();
133
134 memset(&ri, 0, sizeof(ri));
135 ri.magic = JFFS2_MAGIC_BITMASK;
136 ri.nodetype = JFFS2_NODETYPE_INODE;
137 ri.totlen = sizeof(ri);
138 ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
139
140 ri.ino = inode;
141 ri.mode = S_IFDIR | 0755;
142 ri.uid = ri.gid = 0;
143 ri.atime = ri.ctime = ri.mtime = 0;
144 ri.isize = ri.csize = ri.dsize = 0;
145 ri.version = 1;
146 ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
147 ri.data_crc = 0;
148
149 add_data((char *) &ri, sizeof(ri));
150 pad(4);
151 return inode;
152 }
153
154 static void add_file(const char *name, int parent)
155 {
156 int inode, f_offset = 0, fd;
157 struct jffs2_raw_inode ri;
158 struct stat st;
159 char wbuf[4096];
160 const char *fname;
161
162 if (stat(name, &st)) {
163 fprintf(stderr, "File %s does not exist\n", name);
164 return;
165 }
166
167 fname = strrchr(name, '/');
168 if (fname)
169 fname++;
170 else
171 fname = name;
172
173 inode = add_dirent(fname, IFTODT(S_IFREG), parent);
174 memset(&ri, 0, sizeof(ri));
175 ri.magic = JFFS2_MAGIC_BITMASK;
176 ri.nodetype = JFFS2_NODETYPE_INODE;
177
178 ri.ino = inode;
179 ri.mode = st.st_mode;
180 ri.uid = ri.gid = 0;
181 ri.atime = st.st_atime;
182 ri.ctime = st.st_ctime;
183 ri.mtime = st.st_mtime;
184 ri.isize = st.st_size;
185 ri.compr = 0;
186 ri.usercompr = 0;
187
188 fd = open(name, 0);
189 if (fd < 0) {
190 fprintf(stderr, "File %s does not exist\n", name);
191 return;
192 }
193
194 for (;;) {
195 int len = 0;
196
197 for (;;) {
198 len = rbytes() - sizeof(ri);
199 if (len > 128)
200 break;
201
202 pad(erasesize);
203 prep_eraseblock();
204 }
205
206 if (len > sizeof(wbuf))
207 len = sizeof(wbuf);
208
209 len = read(fd, wbuf, len);
210 if (len <= 0)
211 break;
212
213 ri.totlen = sizeof(ri) + len;
214 ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
215 ri.version = ++last_version;
216 ri.offset = f_offset;
217 ri.csize = ri.dsize = len;
218 ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
219 ri.data_crc = crc32(0, wbuf, len);
220 f_offset += len;
221 add_data((char *) &ri, sizeof(ri));
222 add_data(wbuf, len);
223 pad(4);
224 prep_eraseblock();
225 }
226
227 close(fd);
228 }
229
230 int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename)
231 {
232 outfd = fd;
233 mtdofs = ofs;
234
235 buf = malloc(erasesize);
236 target_ino = 1;
237 if (!last_ino)
238 last_ino = 1;
239 add_file(filename, target_ino);
240 pad(erasesize);
241
242 /* add eof marker, pad to eraseblock size and write the data */
243 add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
244 pad(erasesize);
245 free(buf);
246
247 return (mtdofs - ofs);
248 }
249
250 void mtd_parse_jffs2data(const char *buf, const char *dir)
251 {
252 struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
253 unsigned int ofs = 0;
254
255 while (ofs < erasesize) {
256 node = (struct jffs2_unknown_node *) (buf + ofs);
257 if (node->magic != 0x1985)
258 break;
259
260 ofs += PAD(node->totlen);
261 if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
262 struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
263
264 /* is this the right directory name and is it a subdirectory of / */
265 if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize))
266 target_ino = de->ino;
267
268 /* store the last inode and version numbers for adding extra files */
269 if (last_ino < de->ino)
270 last_ino = de->ino;
271 if (last_version < de->version)
272 last_version = de->version;
273 }
274 }
275 }
276
277 int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
278 {
279 int err = -1, fdeof = 0;
280
281 outfd = mtd_check_open(mtd);
282 if (outfd < 0)
283 return -1;
284
285 if (quiet < 2)
286 fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
287
288 buf = malloc(erasesize);
289 if (!buf) {
290 fprintf(stderr, "Out of memory!\n");
291 goto done;
292 }
293
294 if (!*dir)
295 target_ino = 1;
296
297 /* parse the structure of the jffs2 first
298 * locate the directory that the file is going to be placed in */
299 for(;;) {
300 struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
301
302 if (read(outfd, buf, erasesize) != erasesize) {
303 fdeof = 1;
304 break;
305 }
306 mtdofs += erasesize;
307
308 if (node->magic == 0x8519) {
309 fprintf(stderr, "Error: wrong endianness filesystem\n");
310 goto done;
311 }
312
313 /* assume no magic == end of filesystem
314 * the filesystem will probably end with be32(0xdeadc0de) */
315 if (node->magic != 0x1985)
316 break;
317
318 mtd_parse_jffs2data(buf, dir);
319 }
320
321 if (fdeof) {
322 fprintf(stderr, "Error: No room for additional data\n");
323 goto done;
324 }
325
326 /* jump back one eraseblock */
327 mtdofs -= erasesize;
328 lseek(outfd, mtdofs, SEEK_SET);
329
330 ofs = 0;
331
332 if (!last_ino)
333 last_ino = 1;
334
335 if (!target_ino)
336 target_ino = add_dir(dir, 1);
337
338 add_file(filename, target_ino);
339 pad(erasesize);
340
341 /* add eof marker, pad to eraseblock size and write the data */
342 add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
343 pad(erasesize);
344
345 err = 0;
346
347 if (trx_fixup) {
348 trx_fixup(outfd, mtd);
349 }
350
351 done:
352 close(outfd);
353 if (buf)
354 free(buf);
355
356 return err;
357 }