Sfoglia il codice sorgente

cleanup mtd, implement jffs2write - one step closer to config preserving system upgrades

SVN-Revision: 8444
Felix Fietkau 18 anni fa
parent
commit
a91350732c

+ 1 - 1
package/mtd/Makefile

@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=mtd
-PKG_RELEASE:=5
+PKG_RELEASE:=6
 
 PKG_BUILD_DIR := $(KERNEL_BUILD_DIR)/$(PKG_NAME)
 

+ 4 - 10
package/mtd/src/Makefile

@@ -1,12 +1,6 @@
-# $Id$
-
-all: mtd
-
-%.o: %.c
-	$(CC) -I. $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $^
-
-mtd: mtd.o
-	$(CC) -o $@ $^
+CC = gcc
+CFLAGS += -Wall
 
+mtd: mtd.o jffs2.o crc32.o
 clean:
-	rm -f *.o mtd
+	rm -f *.o jffs2 

+ 95 - 0
package/mtd/src/crc32.c

@@ -0,0 +1,95 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};

+ 19 - 0
package/mtd/src/crc32.h

@@ -0,0 +1,19 @@
+#ifndef CRC32_H
+#define CRC32_H
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+	static inline uint32_t
+crc32(uint32_t val, const void *ss, int len)
+{
+	const unsigned char *s = ss;
+	while (--len >= 0)
+		val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+	return val;
+}
+
+#endif

+ 303 - 0
package/mtd/src/jffs2.c

@@ -0,0 +1,303 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "jffs2.h"
+#include "crc32.h"
+#include "mtd.h"
+
+#define PAD(x) (((x)+3)&~3)
+
+#define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
+#define JFFS2_EOF "\xde\xad\xc0\xde"
+
+static int last_ino = 0;
+static int last_version = 0;
+static char *buf = NULL;
+static int ofs = 0;
+static int outfd = 0;
+static int mtdofs = 0;
+
+static void prep_eraseblock(void);
+
+static void pad(int size)
+{
+	if ((ofs % size == 0) && (ofs < erasesize))
+		return;
+
+	if (ofs < erasesize) {
+		memset(buf + ofs, 0xff, (size - (ofs % size)));
+		ofs += (size - (ofs % size));
+	}
+	ofs = ofs % erasesize;
+	if (ofs == 0) {
+		mtd_erase_block(outfd, mtdofs);
+		write(outfd, buf, erasesize);
+		mtdofs += erasesize;
+	}
+}
+
+static inline int rbytes(void)
+{
+	return erasesize - (ofs % erasesize);
+}
+
+static inline void add_data(char *ptr, int len)
+{
+	if (ofs + len > erasesize) {
+		pad(erasesize);
+		prep_eraseblock();
+	}
+	memcpy(buf + ofs, ptr, len);
+	ofs += len;
+}
+
+static void prep_eraseblock(void)
+{
+	if (ofs > 0)
+		return;
+
+	add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
+}
+
+static int add_dirent(char *name, char type, int parent)
+{
+	struct jffs2_raw_dirent *de;
+
+	if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
+		pad(erasesize);
+
+	prep_eraseblock();
+	last_ino++;
+	memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
+	de = (struct jffs2_raw_dirent *) (buf + ofs);
+
+	de->magic = JFFS2_MAGIC_BITMASK;
+	de->nodetype = JFFS2_NODETYPE_DIRENT;
+	de->type = type;
+	de->name_crc = crc32(0, name, strlen(name));
+	de->ino = last_ino++;
+	de->pino = parent;
+	de->totlen = sizeof(*de) + strlen(name);
+	de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
+	de->version = last_version++;
+	de->mctime = 0;
+	de->nsize = strlen(name);
+	de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
+	memcpy(de->name, name, strlen(name));
+
+	ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
+	pad(4);
+
+	return de->ino;
+}
+
+static int add_dir(char *name, int parent)
+{
+	struct jffs2_raw_inode ri;
+	int inode;
+
+	inode = add_dirent(name, IFTODT(S_IFDIR), parent);
+
+	if (rbytes() < sizeof(ri))
+		pad(erasesize);
+	prep_eraseblock();
+
+	memset(&ri, 0, sizeof(ri));
+	ri.magic = JFFS2_MAGIC_BITMASK;
+	ri.nodetype = JFFS2_NODETYPE_INODE;
+	ri.totlen = sizeof(ri);
+	ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+
+	ri.ino = inode;
+	ri.mode = S_IFDIR | 0755;
+	ri.uid = ri.gid = 0;
+	ri.atime = ri.ctime = ri.mtime = 0;
+	ri.isize = ri.csize = ri.dsize = 0;
+	ri.version = 1;
+	ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+	ri.data_crc = 0;
+
+	add_data((char *) &ri, sizeof(ri));
+	pad(4);
+	return inode;
+}
+
+static void add_file(char *name, int parent)
+{
+	int inode, f_offset = 0, fd;
+	struct jffs2_raw_inode ri;
+	struct stat st;
+	char wbuf[4096], *fname;
+	FILE *f;
+
+	if (stat(name, &st)) {
+		fprintf(stderr, "File %s does not exist\n", name);
+		return;
+	}
+
+	fname = strrchr(name, '/');
+	if (fname)
+		fname++;
+	else
+		fname = name;
+
+	inode = add_dirent(name, IFTODT(S_IFREG), parent);
+	memset(&ri, 0, sizeof(ri));
+	ri.magic = JFFS2_MAGIC_BITMASK;
+	ri.nodetype = JFFS2_NODETYPE_INODE;
+
+	ri.ino = inode;
+	ri.mode = st.st_mode;
+	ri.uid = ri.gid = 0;
+	ri.atime = st.st_atime;
+	ri.ctime = st.st_ctime;
+	ri.mtime = st.st_mtime;
+	ri.isize = st.st_size;
+	ri.compr = 0;
+	ri.usercompr = 0;
+
+	fd = open(name, 0);
+	if (fd <= 0) {
+		fprintf(stderr, "File %s does not exist\n", name);
+		return;
+	}
+
+	for (;;) {
+		int len = 0;
+
+		for (;;) {
+			len = rbytes() - sizeof(ri);
+			if (len > 128)
+				break;
+
+			pad(erasesize);
+			prep_eraseblock();
+		}
+
+		if (len > sizeof(wbuf))
+			len = sizeof(wbuf);
+
+		len = read(fd, wbuf, len);
+		if (len <= 0)
+			break;
+
+		ri.totlen = sizeof(ri) + len;
+		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+		ri.version = ++last_version;
+		ri.offset = f_offset;
+		ri.csize = ri.dsize = len;
+		ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+		ri.data_crc = crc32(0, wbuf, len);
+		f_offset += len;
+		add_data((char *) &ri, sizeof(ri));
+		add_data(wbuf, len);
+		pad(4);
+		prep_eraseblock();
+	}
+
+	close(fd);
+}
+
+int mtd_write_jffs2(char *mtd, char *filename, char *dir)
+{
+	int target_ino = 0;
+	int err = -1, fdeof = 0;
+	off_t offset;
+
+	outfd = mtd_check_open(mtd);
+	if (!outfd)
+		return -1;
+
+	if (quiet < 2)
+		fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
+	
+	buf = malloc(erasesize);
+	if (!buf) {
+		fprintf(stderr, "Out of memory!\n");
+		goto done;
+	}
+
+	/* parse the structure of the jffs2 first
+	 * locate the directory that the file is going to be placed in */
+	for(;;) {
+		struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
+		unsigned int ofs = 0;
+
+		if (read(outfd, buf, erasesize) != erasesize) {
+			fdeof = 1;
+			break;
+		}
+		mtdofs += erasesize;
+
+		if (node->magic == 0x8519) {
+			fprintf(stderr, "Error: wrong endianness filesystem\n");
+			goto done;
+		}
+
+		/* assume  no magic == end of filesystem
+		 * the filesystem will probably end with be32(0xdeadc0de) */
+		if (node->magic != 0x1985)
+			break;
+
+		while (ofs < erasesize) {
+			node = (struct jffs2_unknown_node *) (buf + ofs);
+			if (node->magic == 0x1985) {
+				ofs += PAD(node->totlen);
+				if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
+					struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
+					
+					/* is this the right directory name and is it a subdirectory of / */
+					if ((de->pino == 1) && !strncmp(de->name, dir, de->nsize))
+						target_ino = de->ino;
+
+					/* store the last inode and version numbers for adding extra files */
+					if (last_ino < de->ino)
+						last_ino = de->ino;
+					if (last_version < de->version)
+						last_version = de->version;
+				}
+			} else {
+				ofs = ~0;
+			}
+		}
+	}
+
+	if (fdeof) {
+		fprintf(stderr, "Error: No room for additional data\n");
+		goto done;
+	}
+
+	/* jump back one eraseblock */
+	mtdofs -= erasesize;
+	lseek(outfd, mtdofs, SEEK_SET);
+
+	ofs = 0;
+
+	if (!last_ino)
+		last_ino = 1;
+
+	if (!target_ino)
+		target_ino = add_dir(dir, 1);
+
+	add_file(filename, target_ino);
+	pad(erasesize);
+
+	/* add eof marker, pad to eraseblock size and write the data */
+	add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
+	pad(erasesize);
+
+	err = 0;
+
+done:
+	close(outfd);
+	if (buf)
+		free(buf);
+
+	return err;
+}

+ 217 - 0
package/mtd/src/jffs2.h

@@ -0,0 +1,217 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <[email protected]>
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $
+ *
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+#define JFFS2_SUPER_MAGIC   0x72b6
+
+/* You must include something which defines the C99 uintXX_t types. 
+   We don't do it from here because this file is used in too many
+   different environments. */
+
+/* Values we may expect to find in the 'magic' field */
+#define JFFS2_OLD_MAGIC_BITMASK 0x1984
+#define JFFS2_MAGIC_BITMASK 0x1985
+#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC	0x02851885
+
+/* We only allow a single char for length, and 0xFF is empty flash so
+   we don't want it confused with a real length. Hence max 254.
+*/
+#define JFFS2_MAX_NAME_LEN 254
+
+/* How small can we sensibly write nodes? */
+#define JFFS2_MIN_DATA_LEN 128
+
+#define JFFS2_COMPR_NONE	0x00
+#define JFFS2_COMPR_ZERO	0x01
+#define JFFS2_COMPR_RTIME	0x02
+#define JFFS2_COMPR_RUBINMIPS	0x03
+#define JFFS2_COMPR_COPY	0x04
+#define JFFS2_COMPR_DYNRUBIN	0x05
+#define JFFS2_COMPR_ZLIB	0x06
+/* Compatibility flags. */
+#define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+#define JFFS2_NODE_ACCURATE 0x2000
+/* INCOMPAT: Fail to mount the filesystem */
+#define JFFS2_FEATURE_INCOMPAT 0xc000
+/* ROCOMPAT: Mount read-only */
+#define JFFS2_FEATURE_ROCOMPAT 0x8000
+/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
+/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
+
+#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER		1	/* for "user." */
+#define JFFS2_XPREFIX_SECURITY		2	/* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS	3	/* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT	4	/* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED		5	/* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION		0x0001
+
+// Maybe later...
+//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+
+#define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at
+					   mount time, don't wait for it to
+					   happen later */
+#define JFFS2_INO_FLAG_USERCOMPR  2	/* User has requested a specific
+					   compression type */
+
+
+/* These can go once we've made sure we've caught all uses without
+   byteswapping */
+
+typedef	uint32_t jint32_t;
+
+typedef uint32_t jmode_t;
+
+typedef uint16_t jint16_t;
+
+struct jffs2_unknown_node
+{
+	/* All start like this */
+	jint16_t magic;
+	jint16_t nodetype;
+	jint32_t totlen; /* So we can skip over nodes we don't grok */
+	jint32_t hdr_crc;
+};
+
+struct jffs2_raw_dirent
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_DIRENT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t pino;
+	jint32_t version;
+	jint32_t ino; /* == zero for unlink */
+	jint32_t mctime;
+	uint8_t nsize;
+	uint8_t type;
+	uint8_t unused[2];
+	jint32_t node_crc;
+	jint32_t name_crc;
+	uint8_t name[0];
+};
+
+/* The JFFS2 raw inode structure: Used for storage on physical media.  */
+/* The uid, gid, atime, mtime and ctime members could be longer, but
+   are left like this for space efficiency. If and when people decide
+   they really need them extended, it's simple enough to add support for
+   a new type of raw node.
+*/
+struct jffs2_raw_inode
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_INODE */
+	jint32_t totlen;     /* Total length of this node (inc data, etc.) */
+	jint32_t hdr_crc;
+	jint32_t ino;        /* Inode number.  */
+	jint32_t version;    /* Version number.  */
+	jmode_t mode;       /* The file's type or mode.  */
+	jint16_t uid;        /* The file's owner.  */
+	jint16_t gid;        /* The file's group.  */
+	jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
+	jint32_t atime;      /* Last access time.  */
+	jint32_t mtime;      /* Last modification time.  */
+	jint32_t ctime;      /* Change time.  */
+	jint32_t offset;     /* Where to begin to write.  */
+	jint32_t csize;      /* (Compressed) data size */
+	jint32_t dsize;	     /* Size of the node's data. (after decompression) */
+	uint8_t compr;       /* Compression algorithm used */
+	uint8_t usercompr;   /* Compression algorithm requested by the user */
+	jint16_t flags;	     /* See JFFS2_INO_FLAG_* */
+	jint32_t data_crc;   /* CRC for the (compressed) data.  */
+	jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
+	uint8_t data[0];
+};
+
+struct jffs2_raw_xattr {
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XATTR */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t version;
+	uint8_t xprefix;
+	uint8_t name_len;
+	jint16_t value_len;
+	jint32_t data_crc;
+	jint32_t node_crc;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XREF */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t ino;		/* inode number */
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t xseqno;	/* xref sequencial number */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_summary
+{
+	jint16_t magic;
+	jint16_t nodetype; 	/* = JFFS2_NODETYPE_SUMMARY */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t sum_num;	/* number of sum entries*/
+	jint32_t cln_mkr;	/* clean marker size, 0 = no cleanmarker */
+	jint32_t padded;	/* sum of the size of padding nodes */
+	jint32_t sum_crc;	/* summary information crc */
+	jint32_t node_crc; 	/* node crc */
+	jint32_t sum[0]; 	/* inode summary info */
+};
+
+union jffs2_node_union
+{
+	struct jffs2_raw_inode i;
+	struct jffs2_raw_dirent d;
+	struct jffs2_raw_xattr x;
+	struct jffs2_raw_xref r;
+	struct jffs2_raw_summary s;
+	struct jffs2_unknown_node u;
+};
+
+/* Data payload for device nodes. */
+union jffs2_device_node {
+	jint16_t old;
+	jint32_t new;
+};
+
+#endif /* __LINUX_JFFS2_H__ */

+ 0 - 0
package/mtd/src/mtd.h → package/mtd/src/mtd-api.h


+ 171 - 124
package/mtd/src/mtd.c

@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/syscall.h>
 #include <fcntl.h>
@@ -43,7 +44,7 @@
 #include <sys/reboot.h>
 #include <linux/reboot.h>
 
-#include "mtd.h"
+#include "mtd-api.h"
 
 #define TRX_MAGIC       0x30524448      /* "HDR0" */
 #define BUFSIZE (16 * 1024)
@@ -51,6 +52,8 @@
 
 #define DEBUG
 
+#define JFFS2_DEFAULT_DIR	"tmp"
+
 #define SYSTYPE_UNKNOWN     0
 #define SYSTYPE_BROADCOM    1
 /* to be continued */
@@ -63,16 +66,86 @@ struct trx_header {
 	uint32_t offsets[3];    /* Offsets of partitions from start of header */
 };
 
-char buf[BUFSIZE];
-int buflen;
+static char buf[BUFSIZE];
+static char *imagefile;
+static int buflen;
 int quiet;
+int mtdsize = 0;
+int erasesize = 0;
+
+int mtd_open(const char *mtd)
+{
+	FILE *fp;
+	char dev[PATH_MAX];
+	int i;
+	int ret;
+	int flags = O_RDWR | O_SYNC;
+
+	if ((fp = fopen("/proc/mtd", "r"))) {
+		while (fgets(dev, sizeof(dev), fp)) {
+			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
+				snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
+				if ((ret=open(dev, flags))<0) {
+					snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
+					ret=open(dev, flags);
+				}
+				fclose(fp);
+				return ret;
+			}
+		}
+		fclose(fp);
+	}
+
+	return open(mtd, flags);
+}
+
+int mtd_check_open(const char *mtd)
+{
+	struct mtd_info_user mtdInfo;
+	int fd;
+
+	fd = mtd_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		return 0;
+	}
+
+	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
+		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
+		close(fd);
+		return 0;
+	}
+	mtdsize = mtdInfo.size;
+	erasesize = mtdInfo.erasesize;
+
+	return fd;
+}
+
+int mtd_erase_block(int fd, int offset)
+{
+	struct erase_info_user mtdEraseInfo;
+
+	mtdEraseInfo.start = offset;
+	mtdEraseInfo.length = erasesize;
+	ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
+	if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) {
+		fprintf(stderr, "Erasing mtd failed.\n");
+		exit(1);
+	}
+}
+
+int mtd_write_buffer(int fd, char *buf, int offset, int length)
+{
+	lseek(fd, offset, SEEK_SET);
+	write(fd, buf, length);
+}
+
 
 #ifdef target_brcm
-int
+static int
 image_check_brcm(int imagefd, const char *mtd)
 {
 	struct trx_header *trx = (struct trx_header *) buf;
-	struct mtd_info_user mtdInfo;
 	int fd;
 
 	if (strcmp(mtd, "linux") != 0)
@@ -94,18 +167,13 @@ image_check_brcm(int imagefd, const char *mtd)
 	}
 
 	/* check if image fits to mtd device */
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
+	fd = mtd_check_open(mtd);
 	if(fd < 0) {
 		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
 		exit(1);
 	}
 
-	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-		exit(1);
-	}
-		
-	if(mtdInfo.size < trx->len) {
+	if(mtdsize < trx->len) {
 		fprintf(stderr, "Image too big for partition: %s\n", mtd);
 		close(fd);
 		return 0;
@@ -116,7 +184,7 @@ image_check_brcm(int imagefd, const char *mtd)
 }
 #endif /* target_brcm */
 
-int
+static int
 image_check(int imagefd, const char *mtd)
 {
 	int fd, systype;
@@ -129,48 +197,35 @@ image_check(int imagefd, const char *mtd)
 #endif
 }
 
-int mtd_check(char *mtd)
+static int mtd_check(const char *mtd)
 {
-	struct mtd_info_user mtdInfo;
 	int fd;
 
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
-	if(fd < 0) {
-		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+	fd = mtd_check_open(mtd);
+	if (!fd)
 		return 0;
-	}
-
-	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-		close(fd);
-		return 0;
-	}
 
 	close(fd);
 	return 1;
 }
 
-int
+static int
 mtd_unlock(const char *mtd)
 {
 	int fd;
-	struct mtd_info_user mtdInfo;
 	struct erase_info_user mtdLockInfo;
 
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
-	if(fd < 0) {
+	fd = mtd_check_open(mtd);
+	if(fd <= 0) {
 		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
 		exit(1);
 	}
 
-	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-		close(fd);
-		exit(1);
-	}
+	if (quiet < 2) 
+		fprintf(stderr, "Unlocking %s ...\n", mtd);
 
 	mtdLockInfo.start = 0;
-	mtdLockInfo.length = mtdInfo.size;
+	mtdLockInfo.length = mtdsize;
 	if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
 		close(fd);
 		return 0;
@@ -180,56 +235,26 @@ mtd_unlock(const char *mtd)
 	return 0;
 }
 
-int
-mtd_open(const char *mtd, int flags)
-{
-	FILE *fp;
-	char dev[PATH_MAX];
-	int i;
-	int ret;
-
-	if ((fp = fopen("/proc/mtd", "r"))) {
-		while (fgets(dev, sizeof(dev), fp)) {
-			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
-				snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
-				if ((ret=open(dev, flags))<0) {
-					snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
-					ret=open(dev, flags);
-				}
-				fclose(fp);
-				return ret;
-			}
-		}
-		fclose(fp);
-	}
-
-	return open(mtd, flags);
-}
-
-int
+static int
 mtd_erase(const char *mtd)
 {
 	int fd;
-	struct mtd_info_user mtdInfo;
 	struct erase_info_user mtdEraseInfo;
 
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
-	if(fd < 0) {
-		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
-		exit(1);
-	}
+	if (quiet < 2)
+		fprintf(stderr, "Erasing %s ...\n", mtd);
 
-	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-		close(fd);
+	fd = mtd_check_open(mtd);
+	if(fd <= 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
 		exit(1);
 	}
 
-	mtdEraseInfo.length = mtdInfo.erasesize;
+	mtdEraseInfo.length = erasesize;
 
 	for (mtdEraseInfo.start = 0;
-		 mtdEraseInfo.start < mtdInfo.size;
-		 mtdEraseInfo.start += mtdInfo.erasesize) {
+		 mtdEraseInfo.start < mtdsize;
+		 mtdEraseInfo.start += erasesize) {
 		
 		ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
 		if(ioctl(fd, MEMERASE, &mtdEraseInfo))
@@ -241,46 +266,50 @@ mtd_erase(const char *mtd)
 
 }
 
-int
+static int
 mtd_refresh(const char *mtd)
 {
 	int fd;
 
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
-	if(fd < 0) {
+	if (quiet < 2)
+		fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
+
+	fd = mtd_check_open(mtd);
+	if(fd <= 0) {
 		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
 		exit(1);
 	}
+
 	if (ioctl(fd, MTDREFRESH, NULL)) {
 		fprintf(stderr, "Failed to refresh the MTD device\n");
 		close(fd);
 		exit(1);
 	}
 	close(fd);
+
+	if (quiet < 2)
+		fprintf(stderr, "\n");
+
 	return 0;
 }
 
-int
+static int
 mtd_write(int imagefd, const char *mtd)
 {
 	int fd, i, result;
 	size_t r, w, e;
-	struct mtd_info_user mtdInfo;
 	struct erase_info_user mtdEraseInfo;
 	int ret = 0;
 
-	fd = mtd_open(mtd, O_RDWR | O_SYNC);
+	fd = mtd_check_open(mtd);
 	if(fd < 0) {
 		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
 		exit(1);
 	}
-
-	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-		close(fd);
-		exit(1);
-	}
 		
+	if (quiet < 2)
+		fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
+
 	r = w = e = 0;
 	if (!quiet)
 		fprintf(stderr, " [ ]");
@@ -296,17 +325,13 @@ mtd_write(int imagefd, const char *mtd)
 
 		/* need to erase the next block before writing data to it */
 		while (w > e) {
-			mtdEraseInfo.start = e;
-			mtdEraseInfo.length = mtdInfo.erasesize;
-
 			if (!quiet)
 				fprintf(stderr, "\b\b\b[e]");
+
+			mtd_erase_block(fd, e);
+
 			/* erase the chunk */
-			if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
-				fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
-				exit(1);
-			}
-			e += mtdInfo.erasesize;
+			e += erasesize;
 		}
 		
 		if (!quiet)
@@ -327,11 +352,14 @@ mtd_write(int imagefd, const char *mtd)
 	if (!quiet)
 		fprintf(stderr, "\b\b\b\b");
 
+	if (quiet < 2)
+		fprintf(stderr, "\n");
+
 	close(fd);
 	return 0;
 }
 
-void usage(void)
+static void usage(void)
 {
 	fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
 	"The device is in the format of mtdX (eg: mtd4) or its label.\n"
@@ -340,26 +368,44 @@ void usage(void)
 	"        refresh                 refresh mtd partition\n"
 	"        erase                   erase all data on device\n"
 	"        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
+	"        jffs2write <file>       append <file> to the jffs2 partition on the device\n"
 	"Following options are available:\n"
 	"        -q                      quiet mode (once: no [w] on writing,\n"
 	"                                           twice: no status messages)\n"
 	"        -r                      reboot after successful command\n"
 	"        -f                      force write without trx checks\n"
-	"        -e <device>             erase <device> before executing the command\n\n"
+	"        -e <device>             erase <device> before executing the command\n"
+	"        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
+	"\n"
 	"Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
 	"         mtd -r write linux.trx linux\n\n");
 	exit(1);
 }
 
+static void do_reboot(void)
+{
+	fprintf(stderr, "Rebooting ...\n");
+	fflush(stderr);
+
+	/* try regular reboot method first */
+	system("/sbin/reboot");
+	sleep(2);
+
+	/* if we're still alive at this point, force the kernel to reboot */
+	syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
+}
+
 int main (int argc, char **argv)
 {
 	int ch, i, boot, unlock, imagefd, force, unlocked;
-	char *erase[MAX_ARGS], *device, *imagefile;
+	char *erase[MAX_ARGS], *device;
+	char *jffs2dir = JFFS2_DEFAULT_DIR;
 	enum {
 		CMD_ERASE,
 		CMD_WRITE,
 		CMD_UNLOCK,
-		CMD_REFRESH
+		CMD_REFRESH,
+		CMD_JFFS2WRITE
 	} cmd;
 	
 	erase[0] = NULL;
@@ -368,7 +414,7 @@ int main (int argc, char **argv)
 	buflen = 0;
 	quiet = 0;
 
-	while ((ch = getopt(argc, argv, "frqe:")) != -1)
+	while ((ch = getopt(argc, argv, "frqe:d:")) != -1)
 		switch (ch) {
 			case 'f':
 				force = 1;
@@ -387,7 +433,9 @@ int main (int argc, char **argv)
 				erase[i++] = optarg;
 				erase[i] = NULL;
 				break;
-			
+			case 'd':
+				jffs2dir = optarg;
+				break;
 			case '?':
 			default:
 				usage();
@@ -434,6 +482,15 @@ int main (int argc, char **argv)
 				exit(1);
 			}
 		}
+	} else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
+		cmd = CMD_JFFS2WRITE;
+		device = argv[2];
+	
+		imagefile = argv[1];
+		if (!mtd_check(device)) {
+			fprintf(stderr, "Can't open device for writing!\n");
+			exit(1);
+		}
 	} else {
 		usage();
 	}
@@ -443,53 +500,43 @@ int main (int argc, char **argv)
 	i = 0;
 	unlocked = 0;
 	while (erase[i] != NULL) {
-		if (quiet < 2)
-			fprintf(stderr, "Unlocking %s ...\n", erase[i]);
 		mtd_unlock(erase[i]);
-		if (quiet < 2)
-			fprintf(stderr, "Erasing %s ...\n", erase[i]);
 		mtd_erase(erase[i]);
 		if (strcmp(erase[i], device) == 0)
 			unlocked = 1;
 		i++;
 	}
 	
-	if (!unlocked) {
-		if (quiet < 2) 
-			fprintf(stderr, "Unlocking %s ...\n", device);
-		mtd_unlock(device);
-	}
 		
 	switch (cmd) {
 		case CMD_UNLOCK:
+			if (!unlocked)
+				mtd_unlock(device);
 			break;
 		case CMD_ERASE:
-			if (quiet < 2)
-				fprintf(stderr, "Erasing %s ...\n", device);
+			if (!unlocked)
+				mtd_unlock(device);
 			mtd_erase(device);
 			break;
 		case CMD_WRITE:
-			if (quiet < 2)
-				fprintf(stderr, "Writing from %s to %s ... ", imagefile, device);
+			if (!unlocked)
+				mtd_unlock(device);
 			mtd_write(imagefd, device);
-			if (quiet < 2)
-				fprintf(stderr, "\n");
+			break;
+		case CMD_JFFS2WRITE:
+			if (!unlocked)
+				mtd_unlock(device);
+			mtd_write_jffs2(device, imagefile, jffs2dir);
 			break;
 		case CMD_REFRESH:
-			if (quiet < 2)
-				fprintf(stderr, "Refreshing mtd partition %s ... ");
 			mtd_refresh(device);
-			if (quiet < 2)
-				fprintf(stderr, "\n");
 			break;
 	}
 
 	sync();
 	
-	if (boot) {
-		fprintf(stderr, "Rebooting ...\n");
-		fflush(stderr);
-		syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
-	}
+	if (boot)
+		do_reboot();
+
 	return 0;
 }