Root/package/mtd/src/jffs2.c

1/*
2 * jffs2 on-disk structure generator for mtd
3 *
4 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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
41static int last_ino = 0;
42static int last_version = 0;
43static char *buf = NULL;
44static int ofs = 0;
45static int outfd = -1;
46static int mtdofs = 0;
47static int target_ino = 0;
48
49static void prep_eraseblock(void);
50
51static 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        mtd_erase_block(outfd, mtdofs);
63        write(outfd, buf, erasesize);
64        mtdofs += erasesize;
65    }
66}
67
68static inline int rbytes(void)
69{
70    return erasesize - (ofs % erasesize);
71}
72
73static inline void add_data(char *ptr, int len)
74{
75    if (ofs + len > erasesize) {
76        pad(erasesize);
77        prep_eraseblock();
78    }
79    memcpy(buf + ofs, ptr, len);
80    ofs += len;
81}
82
83static void prep_eraseblock(void)
84{
85    if (ofs > 0)
86        return;
87
88    add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
89}
90
91static int add_dirent(const char *name, const char type, int parent)
92{
93    struct jffs2_raw_dirent *de;
94
95    if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
96        pad(erasesize);
97
98    prep_eraseblock();
99    last_ino++;
100    memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
101    de = (struct jffs2_raw_dirent *) (buf + ofs);
102
103    de->magic = JFFS2_MAGIC_BITMASK;
104    de->nodetype = JFFS2_NODETYPE_DIRENT;
105    de->type = type;
106    de->name_crc = crc32(0, name, strlen(name));
107    de->ino = last_ino++;
108    de->pino = parent;
109    de->totlen = sizeof(*de) + strlen(name);
110    de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
111    de->version = last_version++;
112    de->mctime = 0;
113    de->nsize = strlen(name);
114    de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
115    memcpy(de->name, name, strlen(name));
116
117    ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
118    pad(4);
119
120    return de->ino;
121}
122
123static int add_dir(const char *name, int parent)
124{
125    struct jffs2_raw_inode ri;
126    int inode;
127
128    inode = add_dirent(name, IFTODT(S_IFDIR), parent);
129
130    if (rbytes() < sizeof(ri))
131        pad(erasesize);
132    prep_eraseblock();
133
134    memset(&ri, 0, sizeof(ri));
135    ri.magic = JFFS2_MAGIC_BITMASK;
136    ri.nodetype = JFFS2_NODETYPE_INODE;
137    ri.totlen = sizeof(ri);
138    ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
139
140    ri.ino = inode;
141    ri.mode = S_IFDIR | 0755;
142    ri.uid = ri.gid = 0;
143    ri.atime = ri.ctime = ri.mtime = 0;
144    ri.isize = ri.csize = ri.dsize = 0;
145    ri.version = 1;
146    ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
147    ri.data_crc = 0;
148
149    add_data((char *) &ri, sizeof(ri));
150    pad(4);
151    return inode;
152}
153
154static void add_file(const char *name, int parent)
155{
156    int inode, f_offset = 0, fd;
157    struct jffs2_raw_inode ri;
158    struct stat st;
159    char wbuf[4096];
160    const char *fname;
161
162    if (stat(name, &st)) {
163        fprintf(stderr, "File %s does not exist\n", name);
164        return;
165    }
166
167    fname = strrchr(name, '/');
168    if (fname)
169        fname++;
170    else
171        fname = name;
172
173    inode = add_dirent(fname, IFTODT(S_IFREG), parent);
174    memset(&ri, 0, sizeof(ri));
175    ri.magic = JFFS2_MAGIC_BITMASK;
176    ri.nodetype = JFFS2_NODETYPE_INODE;
177
178    ri.ino = inode;
179    ri.mode = st.st_mode;
180    ri.uid = ri.gid = 0;
181    ri.atime = st.st_atime;
182    ri.ctime = st.st_ctime;
183    ri.mtime = st.st_mtime;
184    ri.isize = st.st_size;
185    ri.compr = 0;
186    ri.usercompr = 0;
187
188    fd = open(name, 0);
189    if (fd < 0) {
190        fprintf(stderr, "File %s does not exist\n", name);
191        return;
192    }
193
194    for (;;) {
195        int len = 0;
196
197        for (;;) {
198            len = rbytes() - sizeof(ri);
199            if (len > 128)
200                break;
201
202            pad(erasesize);
203            prep_eraseblock();
204        }
205
206        if (len > sizeof(wbuf))
207            len = sizeof(wbuf);
208
209        len = read(fd, wbuf, len);
210        if (len <= 0)
211            break;
212
213        ri.totlen = sizeof(ri) + len;
214        ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
215        ri.version = ++last_version;
216        ri.offset = f_offset;
217        ri.csize = ri.dsize = len;
218        ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
219        ri.data_crc = crc32(0, wbuf, len);
220        f_offset += len;
221        add_data((char *) &ri, sizeof(ri));
222        add_data(wbuf, len);
223        pad(4);
224        prep_eraseblock();
225    }
226
227    close(fd);
228}
229
230int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename)
231{
232    outfd = fd;
233    mtdofs = ofs;
234
235    buf = malloc(erasesize);
236    target_ino = 1;
237    if (!last_ino)
238        last_ino = 1;
239    add_file(filename, target_ino);
240    pad(erasesize);
241
242    /* add eof marker, pad to eraseblock size and write the data */
243    add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
244    pad(erasesize);
245    free(buf);
246
247    return (mtdofs - ofs);
248}
249
250void mtd_parse_jffs2data(const char *buf, const char *dir)
251{
252    struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
253    unsigned int ofs = 0;
254
255    while (ofs < erasesize) {
256        node = (struct jffs2_unknown_node *) (buf + ofs);
257        if (node->magic != 0x1985)
258            break;
259
260        ofs += PAD(node->totlen);
261        if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
262            struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
263
264            /* is this the right directory name and is it a subdirectory of / */
265            if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize))
266                target_ino = de->ino;
267
268            /* store the last inode and version numbers for adding extra files */
269            if (last_ino < de->ino)
270                last_ino = de->ino;
271            if (last_version < de->version)
272                last_version = de->version;
273        }
274    }
275}
276
277int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
278{
279    int err = -1, fdeof = 0;
280
281    outfd = mtd_check_open(mtd);
282    if (outfd < 0)
283        return -1;
284
285    if (quiet < 2)
286        fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
287    
288    buf = malloc(erasesize);
289    if (!buf) {
290        fprintf(stderr, "Out of memory!\n");
291        goto done;
292    }
293
294    if (!*dir)
295        target_ino = 1;
296
297    /* parse the structure of the jffs2 first
298     * locate the directory that the file is going to be placed in */
299    for(;;) {
300        struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
301
302        if (read(outfd, buf, erasesize) != erasesize) {
303            fdeof = 1;
304            break;
305        }
306        mtdofs += erasesize;
307
308        if (node->magic == 0x8519) {
309            fprintf(stderr, "Error: wrong endianness filesystem\n");
310            goto done;
311        }
312
313        /* assume no magic == end of filesystem
314         * the filesystem will probably end with be32(0xdeadc0de) */
315        if (node->magic != 0x1985)
316            break;
317
318        mtd_parse_jffs2data(buf, dir);
319    }
320
321    if (fdeof) {
322        fprintf(stderr, "Error: No room for additional data\n");
323        goto done;
324    }
325
326    /* jump back one eraseblock */
327    mtdofs -= erasesize;
328    lseek(outfd, mtdofs, SEEK_SET);
329
330    ofs = 0;
331
332    if (!last_ino)
333        last_ino = 1;
334
335    if (!target_ino)
336        target_ino = add_dir(dir, 1);
337
338    add_file(filename, target_ino);
339    pad(erasesize);
340
341    /* add eof marker, pad to eraseblock size and write the data */
342    add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
343    pad(erasesize);
344
345    err = 0;
346
347    if (trx_fixup) {
348      trx_fixup(outfd, mtd);
349    }
350
351done:
352    close(outfd);
353    if (buf)
354        free(buf);
355
356    return err;
357}
358

Archive Download this file



interactive