packages: clean up the package folder
[openwrt.org/openwrt.git] / package / system / mtd / src / jffs2.c
blob:a/package/system/mtd/src/jffs2.c -> blob:b/package/system/mtd/src/jffs2.c
/* /*
* jffs2 on-disk structure generator for mtd * jffs2 on-disk structure generator for mtd
* *
* Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License v2 * modify it under the terms of the GNU General Public License v2
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* Based on: * Based on:
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* Copyright © 2001-2007 Red Hat, Inc. * Copyright © 2001-2007 Red Hat, Inc.
* Created by David Woodhouse <dwmw2@infradead.org> * Created by David Woodhouse <dwmw2@infradead.org>
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <endian.h> #include <endian.h>
#include "jffs2.h" #include "jffs2.h"
#include "crc32.h" #include "crc32.h"
#include "mtd.h" #include "mtd.h"
   
#define PAD(x) (((x)+3)&~3) #define PAD(x) (((x)+3)&~3)
   
#if BYTE_ORDER == BIG_ENDIAN #if BYTE_ORDER == BIG_ENDIAN
# define CLEANMARKER "\x19\x85\x20\x03\x00\x00\x00\x0c\xf0\x60\xdc\x98" # define CLEANMARKER "\x19\x85\x20\x03\x00\x00\x00\x0c\xf0\x60\xdc\x98"
#else #else
# define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4" # define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
#endif #endif
   
static int last_ino = 0; static int last_ino = 0;
static int last_version = 0; static int last_version = 0;
static char *buf = NULL; static char *buf = NULL;
static int ofs = 0; static int ofs = 0;
static int outfd = -1; static int outfd = -1;
static int mtdofs = 0; static int mtdofs = 0;
static int target_ino = 0; static int target_ino = 0;
   
static void prep_eraseblock(void); static void prep_eraseblock(void);
   
static void pad(int size) static void pad(int size)
{ {
if ((ofs % size == 0) && (ofs < erasesize)) if ((ofs % size == 0) && (ofs < erasesize))
return; return;
   
if (ofs < erasesize) { if (ofs < erasesize) {
memset(buf + ofs, 0xff, (size - (ofs % size))); memset(buf + ofs, 0xff, (size - (ofs % size)));
ofs += (size - (ofs % size)); ofs += (size - (ofs % size));
} }
ofs = ofs % erasesize; ofs = ofs % erasesize;
if (ofs == 0) { if (ofs == 0) {
  while (mtd_block_is_bad(outfd, mtdofs) && (mtdofs < mtdsize)) {
  if (!quiet)
  fprintf(stderr, "\nSkipping bad block at 0x%08x ", mtdofs);
   
  mtdofs += erasesize;
   
  /* Move the file pointer along over the bad block. */
  lseek(outfd, erasesize, SEEK_CUR);
  }
mtd_erase_block(outfd, mtdofs); mtd_erase_block(outfd, mtdofs);
write(outfd, buf, erasesize); write(outfd, buf, erasesize);
mtdofs += erasesize; mtdofs += erasesize;
} }
} }
   
static inline int rbytes(void) static inline int rbytes(void)
{ {
return erasesize - (ofs % erasesize); return erasesize - (ofs % erasesize);
} }
   
static inline void add_data(char *ptr, int len) static inline void add_data(char *ptr, int len)
{ {
if (ofs + len > erasesize) { if (ofs + len > erasesize) {
pad(erasesize); pad(erasesize);
prep_eraseblock(); prep_eraseblock();
} }
memcpy(buf + ofs, ptr, len); memcpy(buf + ofs, ptr, len);
ofs += len; ofs += len;
} }
   
static void prep_eraseblock(void) static void prep_eraseblock(void)
{ {
if (ofs > 0) if (ofs > 0)
return; return;
   
add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1); add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
} }
   
static int add_dirent(const char *name, const char type, int parent) static int add_dirent(const char *name, const char type, int parent)
{ {
struct jffs2_raw_dirent *de; struct jffs2_raw_dirent *de;
   
if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name)) if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
pad(erasesize); pad(erasesize);
   
prep_eraseblock(); prep_eraseblock();
last_ino++; last_ino++;
memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent)); memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
de = (struct jffs2_raw_dirent *) (buf + ofs); de = (struct jffs2_raw_dirent *) (buf + ofs);
   
de->magic = JFFS2_MAGIC_BITMASK; de->magic = JFFS2_MAGIC_BITMASK;
de->nodetype = JFFS2_NODETYPE_DIRENT; de->nodetype = JFFS2_NODETYPE_DIRENT;
de->type = type; de->type = type;
de->name_crc = crc32(0, name, strlen(name)); de->name_crc = crc32(0, name, strlen(name));
de->ino = last_ino++; de->ino = last_ino++;
de->pino = parent; de->pino = parent;
de->totlen = sizeof(*de) + strlen(name); de->totlen = sizeof(*de) + strlen(name);
de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4); de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
de->version = last_version++; de->version = last_version++;
de->mctime = 0; de->mctime = 0;
de->nsize = strlen(name); de->nsize = strlen(name);
de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8); de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
memcpy(de->name, name, strlen(name)); memcpy(de->name, name, strlen(name));
   
ofs += sizeof(struct jffs2_raw_dirent) + de->nsize; ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
pad(4); pad(4);
   
return de->ino; return de->ino;
} }
   
static int add_dir(const char *name, int parent) static int add_dir(const char *name, int parent)
{ {
struct jffs2_raw_inode ri; struct jffs2_raw_inode ri;
int inode; int inode;
   
inode = add_dirent(name, IFTODT(S_IFDIR), parent); inode = add_dirent(name, IFTODT(S_IFDIR), parent);
   
if (rbytes() < sizeof(ri)) if (rbytes() < sizeof(ri))
pad(erasesize); pad(erasesize);
prep_eraseblock(); prep_eraseblock();
   
memset(&ri, 0, sizeof(ri)); memset(&ri, 0, sizeof(ri));
ri.magic = JFFS2_MAGIC_BITMASK; ri.magic = JFFS2_MAGIC_BITMASK;
ri.nodetype = JFFS2_NODETYPE_INODE; ri.nodetype = JFFS2_NODETYPE_INODE;
ri.totlen = sizeof(ri); ri.totlen = sizeof(ri);
ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4); ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
   
ri.ino = inode; ri.ino = inode;
ri.mode = S_IFDIR | 0755; ri.mode = S_IFDIR | 0755;
ri.uid = ri.gid = 0; ri.uid = ri.gid = 0;
ri.atime = ri.ctime = ri.mtime = 0; ri.atime = ri.ctime = ri.mtime = 0;
ri.isize = ri.csize = ri.dsize = 0; ri.isize = ri.csize = ri.dsize = 0;
ri.version = 1; ri.version = 1;
ri.node_crc = crc32(0, &ri, sizeof(ri) - 8); ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
ri.data_crc = 0; ri.data_crc = 0;
   
add_data((char *) &ri, sizeof(ri)); add_data((char *) &ri, sizeof(ri));
pad(4); pad(4);
return inode; return inode;
} }
   
static void add_file(const char *name, int parent) static void add_file(const char *name, int parent)
{ {
int inode, f_offset = 0, fd; int inode, f_offset = 0, fd;
struct jffs2_raw_inode ri; struct jffs2_raw_inode ri;
struct stat st; struct stat st;
char wbuf[4096]; char wbuf[4096];
const char *fname; const char *fname;
   
if (stat(name, &st)) { if (stat(name, &st)) {
fprintf(stderr, "File %s does not exist\n", name); fprintf(stderr, "File %s does not exist\n", name);
return; return;
} }
   
fname = strrchr(name, '/'); fname = strrchr(name, '/');
if (fname) if (fname)
fname++; fname++;
else else
fname = name; fname = name;
   
inode = add_dirent(fname, IFTODT(S_IFREG), parent); inode = add_dirent(fname, IFTODT(S_IFREG), parent);
memset(&ri, 0, sizeof(ri)); memset(&ri, 0, sizeof(ri));
ri.magic = JFFS2_MAGIC_BITMASK; ri.magic = JFFS2_MAGIC_BITMASK;
ri.nodetype = JFFS2_NODETYPE_INODE; ri.nodetype = JFFS2_NODETYPE_INODE;
   
ri.ino = inode; ri.ino = inode;
ri.mode = st.st_mode; ri.mode = st.st_mode;
ri.uid = ri.gid = 0; ri.uid = ri.gid = 0;
ri.atime = st.st_atime; ri.atime = st.st_atime;
ri.ctime = st.st_ctime; ri.ctime = st.st_ctime;
ri.mtime = st.st_mtime; ri.mtime = st.st_mtime;
ri.isize = st.st_size; ri.isize = st.st_size;
ri.compr = 0; ri.compr = 0;
ri.usercompr = 0; ri.usercompr = 0;
   
fd = open(name, 0); fd = open(name, 0);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "File %s does not exist\n", name); fprintf(stderr, "File %s does not exist\n", name);
return; return;
} }
   
for (;;) { for (;;) {
int len = 0; int len = 0;
   
for (;;) { for (;;) {
len = rbytes() - sizeof(ri); len = rbytes() - sizeof(ri);
if (len > 128) if (len > 128)
break; break;
   
pad(erasesize); pad(erasesize);
prep_eraseblock(); prep_eraseblock();
} }
   
if (len > sizeof(wbuf)) if (len > sizeof(wbuf))
len = sizeof(wbuf); len = sizeof(wbuf);
   
len = read(fd, wbuf, len); len = read(fd, wbuf, len);
if (len <= 0) if (len <= 0)
break; break;
   
ri.totlen = sizeof(ri) + len; ri.totlen = sizeof(ri) + len;
ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4); ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
ri.version = ++last_version; ri.version = ++last_version;
ri.offset = f_offset; ri.offset = f_offset;
ri.csize = ri.dsize = len; ri.csize = ri.dsize = len;
ri.node_crc = crc32(0, &ri, sizeof(ri) - 8); ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
ri.data_crc = crc32(0, wbuf, len); ri.data_crc = crc32(0, wbuf, len);
f_offset += len; f_offset += len;
add_data((char *) &ri, sizeof(ri)); add_data((char *) &ri, sizeof(ri));
add_data(wbuf, len); add_data(wbuf, len);
pad(4); pad(4);
prep_eraseblock(); prep_eraseblock();
} }
   
close(fd); close(fd);
} }
   
int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename) int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename)
{ {
outfd = fd; outfd = fd;
mtdofs = ofs; mtdofs = ofs;
   
buf = malloc(erasesize); buf = malloc(erasesize);
target_ino = 1; target_ino = 1;
if (!last_ino) if (!last_ino)
last_ino = 1; last_ino = 1;
add_file(filename, target_ino); add_file(filename, target_ino);
pad(erasesize); pad(erasesize);
   
/* add eof marker, pad to eraseblock size and write the data */ /* add eof marker, pad to eraseblock size and write the data */
add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1); add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
pad(erasesize); pad(erasesize);
free(buf); free(buf);
   
return (mtdofs - ofs); return (mtdofs - ofs);
} }
   
void mtd_parse_jffs2data(const char *buf, const char *dir) void mtd_parse_jffs2data(const char *buf, const char *dir)
{ {
struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf; struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
unsigned int ofs = 0; unsigned int ofs = 0;
   
while (ofs < erasesize) { while (ofs < erasesize) {
node = (struct jffs2_unknown_node *) (buf + ofs); node = (struct jffs2_unknown_node *) (buf + ofs);
if (node->magic != 0x1985) if (node->magic != 0x1985)
break; break;
   
ofs += PAD(node->totlen); ofs += PAD(node->totlen);
if (node->nodetype == JFFS2_NODETYPE_DIRENT) { if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node; struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
   
/* is this the right directory name and is it a subdirectory of / */ /* is this the right directory name and is it a subdirectory of / */
if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize)) if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize))
target_ino = de->ino; target_ino = de->ino;
   
/* store the last inode and version numbers for adding extra files */ /* store the last inode and version numbers for adding extra files */
if (last_ino < de->ino) if (last_ino < de->ino)
last_ino = de->ino; last_ino = de->ino;
if (last_version < de->version) if (last_version < de->version)
last_version = de->version; last_version = de->version;
} }
} }
} }
   
int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir) int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
{ {
int err = -1, fdeof = 0; int err = -1, fdeof = 0;
   
outfd = mtd_check_open(mtd); outfd = mtd_check_open(mtd);
if (outfd < 0) if (outfd < 0)
return -1; return -1;
   
if (quiet < 2) if (quiet < 2)
fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd); fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
buf = malloc(erasesize); buf = malloc(erasesize);
if (!buf) { if (!buf) {
fprintf(stderr, "Out of memory!\n"); fprintf(stderr, "Out of memory!\n");
goto done; goto done;
} }
   
if (!*dir) if (!*dir)
target_ino = 1; target_ino = 1;
   
/* parse the structure of the jffs2 first /* parse the structure of the jffs2 first
* locate the directory that the file is going to be placed in */ * locate the directory that the file is going to be placed in */
for(;;) { for(;;) {
struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf; struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
   
if (read(outfd, buf, erasesize) != erasesize) { if (read(outfd, buf, erasesize) != erasesize) {
fdeof = 1; fdeof = 1;
break; break;
} }
mtdofs += erasesize; mtdofs += erasesize;
   
if (node->magic == 0x8519) { if (node->magic == 0x8519) {
fprintf(stderr, "Error: wrong endianness filesystem\n"); fprintf(stderr, "Error: wrong endianness filesystem\n");
goto done; goto done;
} }
   
/* assume no magic == end of filesystem /* assume no magic == end of filesystem
* the filesystem will probably end with be32(0xdeadc0de) */ * the filesystem will probably end with be32(0xdeadc0de) */
if (node->magic != 0x1985) if (node->magic != 0x1985)
break; break;
   
mtd_parse_jffs2data(buf, dir); mtd_parse_jffs2data(buf, dir);
} }
   
if (fdeof) { if (fdeof) {
fprintf(stderr, "Error: No room for additional data\n"); fprintf(stderr, "Error: No room for additional data\n");
goto done; goto done;
} }
   
/* jump back one eraseblock */ /* jump back one eraseblock */
mtdofs -= erasesize; mtdofs -= erasesize;
lseek(outfd, mtdofs, SEEK_SET); lseek(outfd, mtdofs, SEEK_SET);
   
ofs = 0; ofs = 0;
   
if (!last_ino) if (!last_ino)
last_ino = 1; last_ino = 1;
   
if (!target_ino) if (!target_ino)
target_ino = add_dir(dir, 1); target_ino = add_dir(dir, 1);
   
add_file(filename, target_ino); add_file(filename, target_ino);
pad(erasesize); pad(erasesize);
   
/* add eof marker, pad to eraseblock size and write the data */ /* add eof marker, pad to eraseblock size and write the data */
add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1); add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
pad(erasesize); pad(erasesize);
   
err = 0; err = 0;
   
if (trx_fixup) { if (trx_fixup) {
trx_fixup(outfd, mtd); trx_fixup(outfd, mtd);
} }
   
done: done:
close(outfd); close(outfd);
if (buf) if (buf)
free(buf); free(buf);
   
return err; return err;
} }
   
comments