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