add support for appending a file to jffs2 during reflash on the fly
[oweals/openwrt.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
18 static int last_ino = 0;
19 static int last_version = 0;
20 static char *buf = NULL;
21 static int ofs = 0;
22 static int outfd = 0;
23 static int mtdofs = 0;
24 static int target_ino = 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(const char *name, const 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(const 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(const 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];
137         const char *fname;
138         FILE *f;
139
140         if (stat(name, &st)) {
141                 fprintf(stderr, "File %s does not exist\n", name);
142                 return;
143         }
144
145         fname = strrchr(name, '/');
146         if (fname)
147                 fname++;
148         else
149                 fname = name;
150
151         inode = add_dirent(fname, IFTODT(S_IFREG), parent);
152         memset(&ri, 0, sizeof(ri));
153         ri.magic = JFFS2_MAGIC_BITMASK;
154         ri.nodetype = JFFS2_NODETYPE_INODE;
155
156         ri.ino = inode;
157         ri.mode = st.st_mode;
158         ri.uid = ri.gid = 0;
159         ri.atime = st.st_atime;
160         ri.ctime = st.st_ctime;
161         ri.mtime = st.st_mtime;
162         ri.isize = st.st_size;
163         ri.compr = 0;
164         ri.usercompr = 0;
165
166         fd = open(name, 0);
167         if (fd <= 0) {
168                 fprintf(stderr, "File %s does not exist\n", name);
169                 return;
170         }
171
172         for (;;) {
173                 int len = 0;
174
175                 for (;;) {
176                         len = rbytes() - sizeof(ri);
177                         if (len > 128)
178                                 break;
179
180                         pad(erasesize);
181                         prep_eraseblock();
182                 }
183
184                 if (len > sizeof(wbuf))
185                         len = sizeof(wbuf);
186
187                 len = read(fd, wbuf, len);
188                 if (len <= 0)
189                         break;
190
191                 ri.totlen = sizeof(ri) + len;
192                 ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
193                 ri.version = ++last_version;
194                 ri.offset = f_offset;
195                 ri.csize = ri.dsize = len;
196                 ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
197                 ri.data_crc = crc32(0, wbuf, len);
198                 f_offset += len;
199                 add_data((char *) &ri, sizeof(ri));
200                 add_data(wbuf, len);
201                 pad(4);
202                 prep_eraseblock();
203         }
204
205         close(fd);
206 }
207
208 int mtd_replace_jffs2(int fd, int ofs, const char *filename)
209 {
210         outfd = fd;
211         mtdofs = ofs;
212
213         buf = malloc(erasesize);
214         target_ino = 1;
215         if (!last_ino)
216                 last_ino = 1;
217         add_file(filename, target_ino);
218         pad(erasesize);
219
220         /* add eof marker, pad to eraseblock size and write the data */
221         add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
222         pad(erasesize);
223         free(buf);
224 }
225
226 void mtd_parse_jffs2data(const char *buf, const char *dir)
227 {
228         struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
229         unsigned int ofs = 0;
230
231         while (ofs < erasesize) {
232                 node = (struct jffs2_unknown_node *) (buf + ofs);
233                 if (node->magic != 0x1985)
234                         break;
235
236                 ofs += PAD(node->totlen);
237                 if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
238                         struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
239
240                         /* is this the right directory name and is it a subdirectory of / */
241                         if (*dir && (de->pino == 1) && !strncmp(de->name, dir, de->nsize))
242                                 target_ino = de->ino;
243
244                         /* store the last inode and version numbers for adding extra files */
245                         if (last_ino < de->ino)
246                                 last_ino = de->ino;
247                         if (last_version < de->version)
248                                 last_version = de->version;
249                 }
250         }
251 }
252
253 int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
254 {
255         int err = -1, fdeof = 0;
256         off_t offset;
257
258         outfd = mtd_check_open(mtd);
259         if (!outfd)
260                 return -1;
261
262         if (quiet < 2)
263                 fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
264         
265         buf = malloc(erasesize);
266         if (!buf) {
267                 fprintf(stderr, "Out of memory!\n");
268                 goto done;
269         }
270
271         if (!*dir)
272                 target_ino = 1;
273
274         /* parse the structure of the jffs2 first
275          * locate the directory that the file is going to be placed in */
276         for(;;) {
277                 struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
278
279                 if (read(outfd, buf, erasesize) != erasesize) {
280                         fdeof = 1;
281                         break;
282                 }
283                 mtdofs += erasesize;
284
285                 if (node->magic == 0x8519) {
286                         fprintf(stderr, "Error: wrong endianness filesystem\n");
287                         goto done;
288                 }
289
290                 /* assume  no magic == end of filesystem
291                  * the filesystem will probably end with be32(0xdeadc0de) */
292                 if (node->magic != 0x1985)
293                         break;
294
295                 mtd_parse_jffs2data(buf, dir);
296         }
297
298         if (fdeof) {
299                 fprintf(stderr, "Error: No room for additional data\n");
300                 goto done;
301         }
302
303         /* jump back one eraseblock */
304         mtdofs -= erasesize;
305         lseek(outfd, mtdofs, SEEK_SET);
306
307         ofs = 0;
308
309         if (!last_ino)
310                 last_ino = 1;
311
312         if (!target_ino)
313                 target_ino = add_dir(dir, 1);
314
315         add_file(filename, target_ino);
316         pad(erasesize);
317
318         /* add eof marker, pad to eraseblock size and write the data */
319         add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
320         pad(erasesize);
321
322         err = 0;
323
324 done:
325         close(outfd);
326         if (buf)
327                 free(buf);
328
329         return err;
330 }