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