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