rpcd: iwinfo plugin fixes
[openwrt.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 }