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