diff options
| author | Dongsheng Yang <yangds.fnst@cn.fujitsu.com> | 2015-10-31 11:12:01 +0800 | 
|---|---|---|
| committer | Brian Norris <computersforpeace@gmail.com> | 2015-11-11 14:38:40 -0800 | 
| commit | 7d81790ced345585b1e647ca9d0f6678e7062fa4 (patch) | |
| tree | 02f61270c7a0fff7bb6b2e28f247a3d2fd6ff490 /jffsX-utils | |
| parent | 344753f2aacb94d98ce238f81fc4a4b6ef6adea9 (diff) | |
mtd-utils: Restructure the mtd-utils source.
* There is no code modification in this commit, only moving
* the files to proper place.
The user tools looks a little messy as we place almost
the all tools in the root directory of mtd-utils. To make
it more clear, I propose to introduce the following structure
for our source code.
mtd-utils/
	|-- lib
	|-- include
	|-- misc-utils
	|-- jffsX-utils
	|-- nand-utils
	|-- nor-utils
	|-- ubi-utils
	|-- ubifs-utils
	`-- tests
Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'jffsX-utils')
| -rw-r--r-- | jffsX-utils/compr.c | 538 | ||||
| -rw-r--r-- | jffsX-utils/compr.h | 119 | ||||
| -rw-r--r-- | jffsX-utils/compr_lzo.c | 135 | ||||
| -rw-r--r-- | jffsX-utils/compr_rtime.c | 119 | ||||
| -rw-r--r-- | jffsX-utils/compr_zlib.c | 148 | ||||
| -rw-r--r-- | jffsX-utils/device_table.txt | 128 | ||||
| -rw-r--r-- | jffsX-utils/jffs-dump.c | 359 | ||||
| -rw-r--r-- | jffsX-utils/jffs2dump.c | 805 | ||||
| -rw-r--r-- | jffsX-utils/jffs2reader.c | 918 | ||||
| -rw-r--r-- | jffsX-utils/mkfs.jffs2.1 | 268 | ||||
| -rw-r--r-- | jffsX-utils/mkfs.jffs2.c | 1805 | ||||
| -rw-r--r-- | jffsX-utils/rbtree.c | 390 | ||||
| -rw-r--r-- | jffsX-utils/rbtree.h | 171 | ||||
| -rw-r--r-- | jffsX-utils/summary.h | 177 | ||||
| -rw-r--r-- | jffsX-utils/sumtool.c | 872 | 
15 files changed, 6952 insertions, 0 deletions
| diff --git a/jffsX-utils/compr.c b/jffsX-utils/compr.c new file mode 100644 index 0000000..cb4432e --- /dev/null +++ b/jffsX-utils/compr.c @@ -0,0 +1,538 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, + *                    University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory + * in the jffs2 directory. + */ + +#include "compr.h" +#include <string.h> +#include <stdlib.h> +#include <linux/jffs2.h> + +#define FAVOUR_LZO_PERCENT 80 + +extern int page_size; + +/* LIST IMPLEMENTATION (from linux/list.h) */ + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ +	struct list_head name = LIST_HEAD_INIT(name) + +static inline void __list_add(struct list_head *new, +		struct list_head *prev, +		struct list_head *next) +{ +	next->prev = new; +	new->next = next; +	new->prev = prev; +	prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ +	__list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ +	__list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ +	next->prev = prev; +	prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ +	__list_del(entry->prev, entry->next); +	entry->next = (void *) 0; +	entry->prev = (void *) 0; +} + +#define list_entry(ptr, type, member) \ +	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each_entry(pos, head, member)                          \ +	for (pos = list_entry((head)->next, typeof(*pos), member);      \ +			&pos->member != (head);                                    \ +			pos = list_entry(pos->member.next, typeof(*pos), member)) + + +/* Available compressors are on this list */ +static LIST_HEAD(jffs2_compressor_list); + +/* Actual compression mode */ +static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + +void jffs2_set_compression_mode(int mode) +{ +	jffs2_compression_mode = mode; +} + +int jffs2_get_compression_mode(void) +{ +	return jffs2_compression_mode; +} + +/* Statistics for blocks stored without compression */ +static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; + +/* Compression test stuffs */ + +static int jffs2_compression_check = 0; + +static unsigned char *jffs2_compression_check_buf = NULL; + +void jffs2_compression_check_set(int yesno) +{ +	jffs2_compression_check = yesno; +} + +int jffs2_compression_check_get(void) +{ +	return jffs2_compression_check; +} + +static int jffs2_error_cnt = 0; + +int jffs2_compression_check_errorcnt_get(void) +{ +	return jffs2_error_cnt; +} + +#define JFFS2_BUFFER_FILL 0x55 + +/* Called before compression (if compression_check is setted) to prepare +   the buffer for buffer overflow test */ +static void jffs2_decompression_test_prepare(unsigned char *buf, int size) +{ +	memset(buf,JFFS2_BUFFER_FILL,size+1); +} + +/* Called after compression (if compression_check is setted) to test the result */ +static void jffs2_decompression_test(struct jffs2_compressor *compr, +		unsigned char *data_in, unsigned char *output_buf, +		uint32_t cdatalen, uint32_t datalen, uint32_t buf_size) +{ +	uint32_t i; + +	/* buffer overflow test */ +	for (i=buf_size;i>cdatalen;i--) { +		if (output_buf[i]!=JFFS2_BUFFER_FILL) { +			fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. " +					"(bs=%d csize=%d b[%d]=%d)\n", compr->name, +					buf_size, cdatalen, i, (int)(output_buf[i])); +			jffs2_error_cnt++; +			return; +		} +	} +	/* allocing temporary buffer for decompression */ +	if (!jffs2_compression_check_buf) { +		jffs2_compression_check_buf = malloc(page_size); +		if (!jffs2_compression_check_buf) { +			fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n"); +			jffs2_compression_check = 0; +			return; +		} +	} +	/* decompressing */ +	if (!compr->decompress) { +		fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name); +		jffs2_error_cnt++; +		return; +	} +	if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen)) { +		fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name); +		jffs2_error_cnt++; +	} +	/* validate decompression */ +	else { +		for (i=0;i<datalen;i++) { +			if (data_in[i]!=jffs2_compression_check_buf[i]) { +				fprintf(stderr,"JFFS2 compression check: data mismatch at %s (pos %d).\n", compr->name, i); +				jffs2_error_cnt++; +				break; +			} +		} +	} +} + +/* + * Return 1 to use this compression + */ +static int jffs2_is_best_compression(struct jffs2_compressor *this, +		struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) +{ +	switch (jffs2_compression_mode) { +	case JFFS2_COMPR_MODE_SIZE: +		if (bestsize > size) +			return 1; +		return 0; +	case JFFS2_COMPR_MODE_FAVOURLZO: +		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) +			return 1; +		if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) +			return 1; +		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) +			return 1; +		if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) +			return 1; + +		return 0; +	} +	/* Shouldn't happen */ +	return 0; +} + +/* jffs2_compress: + * @data: Pointer to uncompressed data + * @cdata: Pointer to returned pointer to buffer for compressed data + * @datalen: On entry, holds the amount of data available for compression. + *	On exit, expected to hold the amount of data actually compressed. + * @cdatalen: On entry, holds the amount of space available for compressed + *	data. On exit, expected to hold the actual size of the compressed + *	data. + * + * Returns: Lower byte to be stored with data indicating compression type used. + * Zero is used to show that the data could not be compressed - the + * compressed version was actually larger than the original. + * Upper byte will be used later. (soon) + * + * If the cdata buffer isn't large enough to hold all the uncompressed data, + * jffs2_compress should compress as much as will fit, and should set + * *datalen accordingly to show the amount of data which were compressed. + */ +uint16_t jffs2_compress( unsigned char *data_in, unsigned char **cpage_out, +		uint32_t *datalen, uint32_t *cdatalen) +{ +	int ret = JFFS2_COMPR_NONE; +	int compr_ret; +	struct jffs2_compressor *this, *best=NULL; +	unsigned char *output_buf = NULL, *tmp_buf; +	uint32_t orig_slen, orig_dlen; +	uint32_t best_slen=0, best_dlen=0; + +	switch (jffs2_compression_mode) { +		case JFFS2_COMPR_MODE_NONE: +			break; +		case JFFS2_COMPR_MODE_PRIORITY: +			orig_slen = *datalen; +			orig_dlen = *cdatalen; +			output_buf = malloc(orig_dlen+jffs2_compression_check); +			if (!output_buf) { +				fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. Compression failed.\n"); +				goto out; +			} +			list_for_each_entry(this, &jffs2_compressor_list, list) { +				/* Skip decompress-only backwards-compatibility and disabled modules */ +				if ((!this->compress)||(this->disabled)) +					continue; + +				this->usecount++; + +				if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ +					jffs2_decompression_test_prepare(output_buf, orig_dlen); + +				*datalen  = orig_slen; +				*cdatalen = orig_dlen; +				compr_ret = this->compress(data_in, output_buf, datalen, cdatalen); +				this->usecount--; +				if (!compr_ret) { +					ret = this->compr; +					this->stat_compr_blocks++; +					this->stat_compr_orig_size += *datalen; +					this->stat_compr_new_size  += *cdatalen; +					if (jffs2_compression_check) +						jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen); +					break; +				} +			} +			if (ret == JFFS2_COMPR_NONE) free(output_buf); +			break; +		case JFFS2_COMPR_MODE_FAVOURLZO: +		case JFFS2_COMPR_MODE_SIZE: +			orig_slen = *datalen; +			orig_dlen = *cdatalen; +			list_for_each_entry(this, &jffs2_compressor_list, list) { +				uint32_t needed_buf_size; + +				if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) +					needed_buf_size = orig_slen + jffs2_compression_check; +				else +					needed_buf_size = orig_dlen + jffs2_compression_check; + +				/* Skip decompress-only backwards-compatibility and disabled modules */ +				if ((!this->compress)||(this->disabled)) +					continue; +				/* Allocating memory for output buffer if necessary */ +				if ((this->compr_buf_size < needed_buf_size) && (this->compr_buf)) { +					free(this->compr_buf); +					this->compr_buf_size=0; +					this->compr_buf=NULL; +				} +				if (!this->compr_buf) { +					tmp_buf = malloc(needed_buf_size); +					if (!tmp_buf) { +						fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); +						continue; +					} +					else { +						this->compr_buf = tmp_buf; +						this->compr_buf_size = orig_dlen; +					} +				} +				this->usecount++; +				if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ +					jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size); +				*datalen  = orig_slen; +				*cdatalen = orig_dlen; +				compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen); +				this->usecount--; +				if (!compr_ret) { +					if (jffs2_compression_check) +						jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size); +					if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) +								&& (*cdatalen < *datalen)) { +						best_dlen = *cdatalen; +						best_slen = *datalen; +						best = this; +					} +				} +			} +			if (best_dlen) { +				*cdatalen = best_dlen; +				*datalen  = best_slen; +				output_buf = best->compr_buf; +				best->compr_buf = NULL; +				best->compr_buf_size = 0; +				best->stat_compr_blocks++; +				best->stat_compr_orig_size += best_slen; +				best->stat_compr_new_size  += best_dlen; +				ret = best->compr; +			} +			break; +		default: +			fprintf(stderr,"mkfs.jffs2: unknown compression mode.\n"); +	} +out: +	if (ret == JFFS2_COMPR_NONE) { +		*cpage_out = data_in; +		*datalen = *cdatalen; +		none_stat_compr_blocks++; +		none_stat_compr_size += *datalen; +	} +	else { +		*cpage_out = output_buf; +	} +	return ret; +} + + +int jffs2_register_compressor(struct jffs2_compressor *comp) +{ +	struct jffs2_compressor *this; + +	if (!comp->name) { +		fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n"); +		return -1; +	} +	comp->compr_buf_size=0; +	comp->compr_buf=NULL; +	comp->usecount=0; +	comp->stat_compr_orig_size=0; +	comp->stat_compr_new_size=0; +	comp->stat_compr_blocks=0; +	comp->stat_decompr_blocks=0; + +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		if (this->priority < comp->priority) { +			list_add(&comp->list, this->list.prev); +			goto out; +		} +	} +	list_add_tail(&comp->list, &jffs2_compressor_list); +out: +	return 0; +} + +int jffs2_unregister_compressor(struct jffs2_compressor *comp) +{ + +	if (comp->usecount) { +		fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n"); +		return -1; +	} +	list_del(&comp->list); + +	return 0; +} + +#define JFFS2_STAT_BUF_SIZE 16000 + +char *jffs2_list_compressors(void) +{ +	struct jffs2_compressor *this; +	char *buf, *act_buf; + +	act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); +		if ((this->disabled)||(!this->compress)) +			act_buf += sprintf(act_buf,"disabled"); +		else +			act_buf += sprintf(act_buf,"enabled"); +		act_buf += sprintf(act_buf,"\n"); +	} +	return buf; +} + +char *jffs2_stats(void) +{ +	struct jffs2_compressor *this; +	char *buf, *act_buf; + +	act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); + +	act_buf += sprintf(act_buf,"Compression mode: "); +	switch (jffs2_compression_mode) { +		case JFFS2_COMPR_MODE_NONE: +			act_buf += sprintf(act_buf,"none"); +			break; +		case JFFS2_COMPR_MODE_PRIORITY: +			act_buf += sprintf(act_buf,"priority"); +			break; +		case JFFS2_COMPR_MODE_SIZE: +			act_buf += sprintf(act_buf,"size"); +			break; +		case JFFS2_COMPR_MODE_FAVOURLZO: +			act_buf += sprintf(act_buf, "favourlzo"); +			break; +		default: +			act_buf += sprintf(act_buf, "unknown"); +			break; +	} +	act_buf += sprintf(act_buf,"\nCompressors:\n"); +	act_buf += sprintf(act_buf,"%10s             ","none"); +	act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, +			none_stat_compr_size, none_stat_decompr_blocks); +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority); +		if ((this->disabled)||(!this->compress)) +			act_buf += sprintf(act_buf,"- "); +		else +			act_buf += sprintf(act_buf,"+ "); +		act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, +				this->stat_compr_new_size, this->stat_compr_orig_size, +				this->stat_decompr_blocks); +		act_buf += sprintf(act_buf,"\n"); +	} +	return buf; +} + +int jffs2_set_compression_mode_name(const char *name) +{ +	if (!strcmp("none",name)) { +		jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; +		return 0; +	} +	if (!strcmp("priority",name)) { +		jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; +		return 0; +	} +	if (!strcmp("size",name)) { +		jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; +		return 0; +	} +	if (!strcmp("favourlzo", name)) { +		jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; +		return 0; +	} + +	return 1; +} + +static int jffs2_compressor_Xable(const char *name, int disabled) +{ +	struct jffs2_compressor *this; +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		if (!strcmp(this->name, name)) { +			this->disabled = disabled; +			return 0; +		} +	} +	return 1; +} + +int jffs2_enable_compressor_name(const char *name) +{ +	return jffs2_compressor_Xable(name, 0); +} + +int jffs2_disable_compressor_name(const char *name) +{ +	return jffs2_compressor_Xable(name, 1); +} + +int jffs2_set_compressor_priority(const char *name, int priority) +{ +	struct jffs2_compressor *this,*comp; +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		if (!strcmp(this->name, name)) { +			this->priority = priority; +			comp = this; +			goto reinsert; +		} +	} +	fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name); +	return 1; +reinsert: +	/* list is sorted in the order of priority, so if +	   we change it we have to reinsert it into the +	   good place */ +	list_del(&comp->list); +	list_for_each_entry(this, &jffs2_compressor_list, list) { +		if (this->priority < comp->priority) { +			list_add(&comp->list, this->list.prev); +			return 0; +		} +	} +	list_add_tail(&comp->list, &jffs2_compressor_list); +	return 0; +} + + +int jffs2_compressors_init(void) +{ +#ifdef CONFIG_JFFS2_ZLIB +	jffs2_zlib_init(); +#endif +#ifdef CONFIG_JFFS2_RTIME +	jffs2_rtime_init(); +#endif +#ifdef CONFIG_JFFS2_LZO +	jffs2_lzo_init(); +#endif +	return 0; +} + +int jffs2_compressors_exit(void) +{ +#ifdef CONFIG_JFFS2_RTIME +	jffs2_rtime_exit(); +#endif +#ifdef CONFIG_JFFS2_ZLIB +	jffs2_zlib_exit(); +#endif +#ifdef CONFIG_JFFS2_LZO +	jffs2_lzo_exit(); +#endif +	return 0; +} diff --git a/jffsX-utils/compr.h b/jffsX-utils/compr.h new file mode 100644 index 0000000..a21e935 --- /dev/null +++ b/jffsX-utils/compr.h @@ -0,0 +1,119 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, + *                    University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + */ + +#ifndef __JFFS2_COMPR_H__ +#define __JFFS2_COMPR_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "linux/jffs2.h" + +#define CONFIG_JFFS2_ZLIB +#define CONFIG_JFFS2_RTIME +#define CONFIG_JFFS2_LZO + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY  20 +#define JFFS2_RTIME_PRIORITY     50 +#define JFFS2_ZLIB_PRIORITY      60 +#define JFFS2_LZO_PRIORITY       80 + +#define JFFS2_COMPR_MODE_NONE       0 +#define JFFS2_COMPR_MODE_PRIORITY   1 +#define JFFS2_COMPR_MODE_SIZE       2 +#define JFFS2_COMPR_MODE_FAVOURLZO  3 + +#define kmalloc(a,b)                malloc(a) +#define kfree(a)                    free(a) +#ifndef GFP_KERNEL +#define GFP_KERNEL                  0 +#endif + +#define vmalloc(a)                  malloc(a) +#define vfree(a)                    free(a) + +#define printk(...)                 fprintf(stderr,__VA_ARGS__) + +#define KERN_EMERG +#define KERN_ALERT +#define KERN_CRIT +#define KERN_ERR +#define KERN_WARNING +#define KERN_NOTICE +#define KERN_INFO +#define KERN_DEBUG + +struct list_head { +	struct list_head *next, *prev; +}; + +void jffs2_set_compression_mode(int mode); +int jffs2_get_compression_mode(void); +int jffs2_set_compression_mode_name(const char *mode_name); + +int jffs2_enable_compressor_name(const char *name); +int jffs2_disable_compressor_name(const char *name); + +int jffs2_set_compressor_priority(const char *name, int priority); + +struct jffs2_compressor { +	struct list_head list; +	int priority;             /* used by prirority comr. mode */ +	const char *name; +	char compr;               /* JFFS2_COMPR_XXX */ +	int (*compress)(unsigned char *data_in, unsigned char *cpage_out, +			uint32_t *srclen, uint32_t *destlen); +	int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, +			uint32_t cdatalen, uint32_t datalen); +	int usecount; +	int disabled;             /* if seted the compressor won't compress */ +	unsigned char *compr_buf; /* used by size compr. mode */ +	uint32_t compr_buf_size;  /* used by size compr. mode */ +	uint32_t stat_compr_orig_size; +	uint32_t stat_compr_new_size; +	uint32_t stat_compr_blocks; +	uint32_t stat_decompr_blocks; +}; + +int jffs2_register_compressor(struct jffs2_compressor *comp); +int jffs2_unregister_compressor(struct jffs2_compressor *comp); + +int jffs2_compressors_init(void); +int jffs2_compressors_exit(void); + +uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, +		uint32_t *datalen, uint32_t *cdatalen); + +/* If it is setted, a decompress will be called after every compress */ +void jffs2_compression_check_set(int yesno); +int jffs2_compression_check_get(void); +int jffs2_compression_check_errorcnt_get(void); + +char *jffs2_list_compressors(void); +char *jffs2_stats(void); + +/* Compressor modules */ + +/* These functions will be called by jffs2_compressors_init/exit */ +#ifdef CONFIG_JFFS2_ZLIB +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); +#endif +#ifdef CONFIG_JFFS2_RTIME +int jffs2_rtime_init(void); +void jffs2_rtime_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif + +#endif /* __JFFS2_COMPR_H__ */ diff --git a/jffsX-utils/compr_lzo.c b/jffsX-utils/compr_lzo.c new file mode 100644 index 0000000..d2e2afc --- /dev/null +++ b/jffsX-utils/compr_lzo.c @@ -0,0 +1,135 @@ +/* + * JFFS2 LZO Compression Interface. + * + * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * + * Author: Richard Purdie <rpurdie@openedhand.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#ifndef WITHOUT_LZO +#include <asm/types.h> +#include <linux/jffs2.h> +#include <lzo/lzo1x.h> +#include "compr.h" + +extern int page_size; + +static void *lzo_mem; +static void *lzo_compress_buf; + +/* + * Note about LZO compression. + * + * We want to use the _999_ compression routine which gives better compression + * rates at the expense of time. Decompression time is unaffected. We might as + * well use the standard lzo library routines for this but they will overflow + * the destination buffer since they don't check the destination size. + * + * We therefore compress to a temporary buffer and copy if it will fit. + * + */ +static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, +			  uint32_t *sourcelen, uint32_t *dstlen) +{ +	lzo_uint compress_size; +	int ret; + +	ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); + +	if (ret != LZO_E_OK) +		return -1; + +	if (compress_size > *dstlen) +		return -1; + +	memcpy(cpage_out, lzo_compress_buf, compress_size); +	*dstlen = compress_size; + +	return 0; +} + +static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, +				 uint32_t srclen, uint32_t destlen) +{ +	int ret; +	lzo_uint dl; + +	ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); + +	if (ret != LZO_E_OK || dl != destlen) +		return -1; + +	return 0; +} + +static struct jffs2_compressor jffs2_lzo_comp = { +	.priority = JFFS2_LZO_PRIORITY, +	.name = "lzo", +	.compr = JFFS2_COMPR_LZO, +	.compress = &jffs2_lzo_cmpr, +	.decompress = &jffs2_lzo_decompress, +	.disabled = 1, +}; + +int jffs2_lzo_init(void) +{ +	int ret; + +	lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); +	if (!lzo_mem) +		return -1; + +	/* Worse case LZO compression size from their FAQ */ +	lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); +	if (!lzo_compress_buf) { +		free(lzo_mem); +		return -1; +	} + +	ret = jffs2_register_compressor(&jffs2_lzo_comp); +	if (ret < 0) { +		free(lzo_compress_buf); +		free(lzo_mem); +	} + +	return ret; +} + +void jffs2_lzo_exit(void) +{ +	jffs2_unregister_compressor(&jffs2_lzo_comp); +	free(lzo_compress_buf); +	free(lzo_mem); +} + +#else + +int jffs2_lzo_init(void) +{ +	return 0; +} + +void jffs2_lzo_exit(void) +{ +} + +#endif diff --git a/jffsX-utils/compr_rtime.c b/jffsX-utils/compr_rtime.c new file mode 100644 index 0000000..f24379d --- /dev/null +++ b/jffsX-utils/compr_rtime.c @@ -0,0 +1,119 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@redhat.com> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last + * occurrences" for every possible source-value; after sending the + * first source-byte, the second byte indicated the "run" length of + * matches + * + * The algorithm is intended to only send "whole bytes", no bit-messing. + * + */ + +#include <stdint.h> +#include <string.h> +#include "compr.h" + +/* _compress returns the compressed size, -1 if bigger */ +static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, +		uint32_t *sourcelen, uint32_t *dstlen) +{ +	short positions[256]; +	int outpos = 0; +	int pos=0; + +	memset(positions,0,sizeof(positions)); + +	while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { +		int backpos, runlen=0; +		unsigned char value; + +		value = data_in[pos]; + +		cpage_out[outpos++] = data_in[pos++]; + +		backpos = positions[value]; +		positions[value]=pos; + +		while ((backpos < pos) && (pos < (*sourcelen)) && +				(data_in[pos]==data_in[backpos++]) && (runlen<255)) { +			pos++; +			runlen++; +		} +		cpage_out[outpos++] = runlen; +	} + +	if (outpos >= pos) { +		/* We failed */ +		return -1; +	} + +	/* Tell the caller how much we managed to compress, and how much space it took */ +	*sourcelen = pos; +	*dstlen = outpos; +	return 0; +} + + +static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, +		__attribute__((unused)) uint32_t srclen, uint32_t destlen) +{ +	short positions[256]; +	int outpos = 0; +	int pos=0; + +	memset(positions,0,sizeof(positions)); + +	while (outpos<destlen) { +		unsigned char value; +		int backoffs; +		int repeat; + +		value = data_in[pos++]; +		cpage_out[outpos++] = value; /* first the verbatim copied byte */ +		repeat = data_in[pos++]; +		backoffs = positions[value]; + +		positions[value]=outpos; +		if (repeat) { +			if (backoffs + repeat >= outpos) { +				while(repeat) { +					cpage_out[outpos++] = cpage_out[backoffs++]; +					repeat--; +				} +			} else { +				memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat); +				outpos+=repeat; +			} +		} +	} +	return 0; +} + + +static struct jffs2_compressor jffs2_rtime_comp = { +	.priority = JFFS2_RTIME_PRIORITY, +	.name = "rtime", +	.disabled = 0, +	.compr = JFFS2_COMPR_RTIME, +	.compress = &jffs2_rtime_compress, +	.decompress = &jffs2_rtime_decompress, +}; + +int jffs2_rtime_init(void) +{ +	return jffs2_register_compressor(&jffs2_rtime_comp); +} + +void jffs2_rtime_exit(void) +{ +	jffs2_unregister_compressor(&jffs2_rtime_comp); +} diff --git a/jffsX-utils/compr_zlib.c b/jffsX-utils/compr_zlib.c new file mode 100644 index 0000000..1f94628 --- /dev/null +++ b/jffsX-utils/compr_zlib.c @@ -0,0 +1,148 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * + * The original JFFS, from which the design for JFFS2 was derived, + * was designed and implemented by Axis Communications AB. + * + * The contents of this file are subject to the Red Hat eCos Public + * License Version 1.1 (the "Licence"); you may not use this file + * except in compliance with the Licence.  You may obtain a copy of + * the Licence at http://www.redhat.com/ + * + * Software distributed under the Licence is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the Licence for the specific language governing rights and + * limitations under the Licence. + * + * The Original Code is JFFS2 - Journalling Flash File System, version 2 + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above.  If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the RHEPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL.  If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the RHEPL or the GPL. + */ + +#define PROGRAM_NAME "compr_zlib" + +#include <stdint.h> +#define crc32 __zlib_crc32 +#include <zlib.h> +#undef crc32 +#include <stdio.h> +#include <asm/types.h> +#include <linux/jffs2.h> +#include "common.h" +#include "compr.h" + +/* Plan: call deflate() with avail_in == *sourcelen, +   avail_out = *dstlen - 12 and flush == Z_FINISH. +   If it doesn't manage to finish,	call it again with +   avail_in == 0 and avail_out set to the remaining 12 +   bytes for it to clean up. +Q: Is 12 bytes sufficient? + */ +#define STREAM_END_SPACE 12 + +static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, +		uint32_t *sourcelen, uint32_t *dstlen) +{ +	z_stream strm; +	int ret; + +	if (*dstlen <= STREAM_END_SPACE) +		return -1; + +	strm.zalloc = (void *)0; +	strm.zfree = (void *)0; + +	if (Z_OK != deflateInit(&strm, 3)) { +		return -1; +	} +	strm.next_in = data_in; +	strm.total_in = 0; + +	strm.next_out = cpage_out; +	strm.total_out = 0; + +	while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { +		strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); +		strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); +		ret = deflate(&strm, Z_PARTIAL_FLUSH); +		if (ret != Z_OK) { +			deflateEnd(&strm); +			return -1; +		} +	} +	strm.avail_out += STREAM_END_SPACE; +	strm.avail_in = 0; +	ret = deflate(&strm, Z_FINISH); +	if (ret != Z_STREAM_END) { +		deflateEnd(&strm); +		return -1; +	} +	deflateEnd(&strm); + +	if (strm.total_out >= strm.total_in) +		return -1; + + +	*dstlen = strm.total_out; +	*sourcelen = strm.total_in; +	return 0; +} + +static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, +		uint32_t srclen, uint32_t destlen) +{ +	z_stream strm; +	int ret; + +	strm.zalloc = (void *)0; +	strm.zfree = (void *)0; + +	if (Z_OK != inflateInit(&strm)) { +		return 1; +	} +	strm.next_in = data_in; +	strm.avail_in = srclen; +	strm.total_in = 0; + +	strm.next_out = cpage_out; +	strm.avail_out = destlen; +	strm.total_out = 0; + +	while((ret = inflate(&strm, Z_FINISH)) == Z_OK) +		; + +	inflateEnd(&strm); +	return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { +	.priority = JFFS2_ZLIB_PRIORITY, +	.name = "zlib", +	.disabled = 0, +	.compr = JFFS2_COMPR_ZLIB, +	.compress = &jffs2_zlib_compress, +	.decompress = &jffs2_zlib_decompress, +}; + +int jffs2_zlib_init(void) +{ +	return jffs2_register_compressor(&jffs2_zlib_comp); +} + +void jffs2_zlib_exit(void) +{ +	jffs2_unregister_compressor(&jffs2_zlib_comp); +} diff --git a/jffsX-utils/device_table.txt b/jffsX-utils/device_table.txt new file mode 100644 index 0000000..394a62b --- /dev/null +++ b/jffsX-utils/device_table.txt @@ -0,0 +1,128 @@ +# This is a sample device table file for use with mkfs.jffs2.  You can +# do all sorts of interesting things with a device table file.  For +# example, if you want to adjust the permissions on a particular file +# you can just add an entry like: +#   /sbin/foobar	f	2755	0	0	-	-	-	-	- +# and (assuming the file /sbin/foobar exists) it will be made setuid +# root (regardless of what its permissions are on the host filesystem. +# +# Device table entries take the form of: +# <name>		<type>	<mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count> +# where name is the file name,  type can be one of: +#	f	A regular file +#	d	Directory +#	c	Character special device file +#	b	Block special device file +#	p	Fifo (named pipe) +# uid is the user id for the target file, gid is the group id for the +# target file.  The rest of the entried apply only to device special +# file. + +# When building a target filesystem, it is desirable to not have to +# become root and then run 'mknod' a thousand times.  Using a device +# table you can create device nodes and directories "on the fly". +# Furthermore, you can use a single table entry to create a many device +# minors.  For example, if I wanted to create /dev/hda and /dev/hda[0-15] +# I could just use the following two table entries: +#   /dev/hda	b	640	0	0	3	0	0	0	- +#   /dev/hda	b	640	0	0	3	1	1	1	15 +# +# Have fun +# -Erik Andersen <andersen@codepoet.org> +# + +#<name>		<type>	<mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count> +/dev		d	755	0	0	-	-	-	-	- +/dev/mem	c	640	0	0	1	1	0	0	- +/dev/kmem	c	640	0	0	1	2	0	0	- +/dev/null	c	640	0	0	1	3	0	0	- +/dev/zero	c	640	0	0	1	5	0	0	- +/dev/random	c	640	0	0	1	8	0	0	- +/dev/urandom	c	640	0	0	1	9	0	0	- +/dev/tty	c	666	0	0	5	0	0	0	- +/dev/tty	c	666	0	0	4	0	0	1	6 +/dev/console	c	640	0	0	5	1	0	0	- +/dev/ram	b	640	0	0	1	1	0	0	- +/dev/ram	b	640	0	0	1	0	0	1	4 +/dev/loop	b	640	0	0	7	0	0	1	2 +/dev/ptmx	c	666	0	0	5	2	0	0	- +#/dev/ttyS	c	640	0	0	4	64	0	1	4 +#/dev/psaux	c	640	0	0	10	1	0	0	- +#/dev/rtc	c	640	0	0	10	135	0	0	- + +# Adjust permissions on some normal files +#/etc/shadow	f	600	0	0	-	-	-	-	- +#/bin/tinylogin	f	4755	0	0	-	-	-	-	- + +# User-mode Linux stuff +/dev/ubda	b	640	0	0	98	0	0	0	- +/dev/ubda	b	640	0	0	98	1	1	1	15 + +# IDE Devices +/dev/hda	b	640	0	0	3	0	0	0	- +/dev/hda	b	640	0	0	3	1	1	1	15 +/dev/hdb	b	640	0	0	3	64	0	0	- +/dev/hdb	b	640	0	0	3	65	1	1	15 +#/dev/hdc	b	640	0	0	22	0	0	0	- +#/dev/hdc	b	640	0	0	22	1	1	1	15 +#/dev/hdd	b	640	0	0	22	64	0	0	- +#/dev/hdd	b	640	0	0	22	65	1	1	15 +#/dev/hde	b	640	0	0	33	0	0	0	- +#/dev/hde	b	640	0	0	33	1	1	1	15 +#/dev/hdf	b	640	0	0	33	64	0	0	- +#/dev/hdf	b	640	0	0	33	65	1	1	15 +#/dev/hdg	b	640	0	0	34	0	0	0	- +#/dev/hdg	b	640	0	0	34	1	1	1	15 +#/dev/hdh	b	640	0	0	34	64	0	0	- +#/dev/hdh	b	640	0	0	34	65	1	1	15 + +# SCSI Devices +#/dev/sda	b	640	0	0	8	0	0	0	- +#/dev/sda	b	640	0	0	8	1	1	1	15 +#/dev/sdb	b	640	0	0	8	16	0	0	- +#/dev/sdb	b	640	0	0	8	17	1	1	15 +#/dev/sdc	b	640	0	0	8	32	0	0	- +#/dev/sdc	b	640	0	0	8	33	1	1	15 +#/dev/sdd	b	640	0	0	8	48	0	0	- +#/dev/sdd	b	640	0	0	8	49	1	1	15 +#/dev/sde	b	640	0	0	8	64	0	0	- +#/dev/sde	b	640	0	0	8	65	1	1	15 +#/dev/sdf	b	640	0	0	8	80	0	0	- +#/dev/sdf	b	640	0	0	8	81	1	1	15 +#/dev/sdg	b	640	0	0	8	96	0	0	- +#/dev/sdg	b	640	0	0	8	97	1	1	15 +#/dev/sdh	b	640	0	0	8	112	0	0	- +#/dev/sdh	b	640	0	0	8	113	1	1	15 +#/dev/sg		c	640	0	0	21	0	0	1	15 +#/dev/scd	b	640	0	0	11	0	0	1	15 +#/dev/st		c	640	0	0	9	0	0	1	8 +#/dev/nst	c	640	0	0	9	128	0	1	8 +#/dev/st	c	640	0	0	9	32	1	1	4 +#/dev/st	c	640	0	0	9	64	1	1	4 +#/dev/st	c	640	0	0	9	96	1	1	4 + +# Floppy disk devices +#/dev/fd		b	640	0	0	2	0	0	1	2 +#/dev/fd0d360	b	640	0	0	2	4	0	0	- +#/dev/fd1d360	b	640	0	0	2	5	0	0	- +#/dev/fd0h1200	b	640	0	0	2	8	0	0	- +#/dev/fd1h1200	b	640	0	0	2	9	0	0	- +#/dev/fd0u1440	b	640	0	0	2	28	0	0	- +#/dev/fd1u1440	b	640	0	0	2	29	0	0	- +#/dev/fd0u2880	b	640	0	0	2	32	0	0	- +#/dev/fd1u2880	b	640	0	0	2	33	0	0	- + +# All the proprietary cdrom devices in the world +#/dev/aztcd	b	640	0	0	29	0	0	0	- +#/dev/bpcd	b	640	0	0	41	0	0	0	- +#/dev/capi20	c	640	0	0	68	0	0	1	2 +#/dev/cdu31a	b	640	0	0	15	0	0	0	- +#/dev/cdu535	b	640	0	0	24	0	0	0	- +#/dev/cm206cd	b	640	0	0	32	0	0	0	- +#/dev/sjcd	b	640	0	0	18	0	0	0	- +#/dev/sonycd	b	640	0	0	15	0	0	0	- +#/dev/gscd	b	640	0	0	16	0	0	0	- +#/dev/sbpcd	b	640	0	0	25	0	0	0	- +#/dev/sbpcd	b	640	0	0	25	0	0	1	4 +#/dev/mcd	b	640	0	0	23	0	0	0	- +#/dev/optcd	b	640	0	0	17	0	0	0	- diff --git a/jffsX-utils/jffs-dump.c b/jffsX-utils/jffs-dump.c new file mode 100644 index 0000000..3176469 --- /dev/null +++ b/jffsX-utils/jffs-dump.c @@ -0,0 +1,359 @@ +/* + * Dump JFFS filesystem. + * Useful when it buggers up. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <linux/types.h> +#include <asm/byteorder.h> + +#include "common.h" + +#define BLOCK_SIZE 1024 +#define JFFS_MAGIC 0x34383931 /* "1984" */ +#define JFFS_MAX_NAME_LEN 256 +#define JFFS_MIN_INO 1 +#define JFFS_TRACE_INDENT 4 +#define JFFS_ALIGN_SIZE 4 +#define MAX_CHUNK_SIZE 32768 + +/* How many padding bytes should be inserted between two chunks of data +   on the flash?  */ +#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE                     \ +			- ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ +		% JFFS_ALIGN_SIZE) + +#define JFFS_EMPTY_BITMASK 0xffffffff +#define JFFS_MAGIC_BITMASK 0x34383931 +#define JFFS_DIRTY_BITMASK 0x00000000 + +struct jffs_raw_inode +{ +	uint32_t magic;    /* A constant magic number.  */ +	uint32_t ino;      /* Inode number.  */ +	uint32_t pino;     /* Parent's inode number.  */ +	uint32_t version;  /* Version number.  */ +	uint32_t mode;     /* file_type, mode  */ +	uint16_t uid; +	uint16_t gid; +	uint32_t atime; +	uint32_t mtime; +	uint32_t ctime; +	uint32_t offset;     /* Where to begin to write.  */ +	uint32_t dsize;      /* Size of the file data.  */ +	uint32_t rsize;      /* How much are going to be replaced?  */ +	uint8_t nsize;       /* Name length.  */ +	uint8_t nlink;       /* Number of links.  */ +	uint8_t spare : 6;   /* For future use.  */ +	uint8_t rename : 1;  /* Is this a special rename?  */ +	uint8_t deleted : 1; /* Has this file been deleted?  */ +	uint8_t accurate;    /* The inode is obsolete if accurate == 0.  */ +	uint32_t dchksum;    /* Checksum for the data.  */ +	uint16_t nchksum;    /* Checksum for the name.  */ +	uint16_t chksum;     /* Checksum for the raw_inode.  */ +}; + + +struct jffs_file +{ +	struct jffs_raw_inode inode; +	char *name; +	unsigned char *data; +}; + + +char *root_directory_name = NULL; +int fs_pos = 0; +int verbose = 0; + +#define ENDIAN_HOST   0 +#define ENDIAN_BIG    1 +#define ENDIAN_LITTLE 2 +int endian = ENDIAN_HOST; + +static uint32_t jffs_checksum(void *data, int size); +void jffs_print_trace(const char *path, int depth); +int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, +		int depth); +void write_file(struct jffs_file *f, FILE *fs, struct stat st); +void read_data(struct jffs_file *f, const char *path, int offset); +int mkfs(FILE *fs, const char *path, int ino, int parent, int depth); + + +	static uint32_t +jffs_checksum(void *data, int size) +{ +	uint32_t sum = 0; +	uint8_t *ptr = (uint8_t *)data; + +	while (size-- > 0) +	{ +		sum += *ptr++; +	} + +	return sum; +} + + +	void +jffs_print_trace(const char *path, int depth) +{ +	int path_len = strlen(path); +	int out_pos = depth * JFFS_TRACE_INDENT; +	int pos = path_len - 1; +	char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1); + +	if (verbose >= 2) +	{ +		fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path); +	} + +	if (!out) { +		fprintf(stderr, "jffs_print_trace(): Allocation failed.\n"); +		fprintf(stderr, " path: \"%s\"\n", path); +		fprintf(stderr, "depth: %d\n", depth); +		exit(1); +	} + +	memset(out, ' ', depth * JFFS_TRACE_INDENT); + +	if (path[pos] == '/') +	{ +		pos--; +	} +	while (path[pos] && (path[pos] != '/')) +	{ +		pos--; +	} +	for (pos++; path[pos] && (path[pos] != '/'); pos++) +	{ +		out[out_pos++] = path[pos]; +	} +	out[out_pos] = '\0'; +	fprintf(stderr, "%s\n", out); +} + + +/* Print the contents of a raw inode.  */ +	void +jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) +{ +	fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version); +	fprintf(stdout, "{\n"); +	fprintf(stdout, "        0x%08x, /* magic  */\n", raw_inode->magic); +	fprintf(stdout, "        0x%08x, /* ino  */\n", raw_inode->ino); +	fprintf(stdout, "        0x%08x, /* pino  */\n", raw_inode->pino); +	fprintf(stdout, "        0x%08x, /* version  */\n", raw_inode->version); +	fprintf(stdout, "        0x%08x, /* mode  */\n", raw_inode->mode); +	fprintf(stdout, "        0x%04x,     /* uid  */\n", raw_inode->uid); +	fprintf(stdout, "        0x%04x,     /* gid  */\n", raw_inode->gid); +	fprintf(stdout, "        0x%08x, /* atime  */\n", raw_inode->atime); +	fprintf(stdout, "        0x%08x, /* mtime  */\n", raw_inode->mtime); +	fprintf(stdout, "        0x%08x, /* ctime  */\n", raw_inode->ctime); +	fprintf(stdout, "        0x%08x, /* offset  */\n", raw_inode->offset); +	fprintf(stdout, "        0x%08x, /* dsize  */\n", raw_inode->dsize); +	fprintf(stdout, "        0x%08x, /* rsize  */\n", raw_inode->rsize); +	fprintf(stdout, "        0x%02x,       /* nsize  */\n", raw_inode->nsize); +	fprintf(stdout, "        0x%02x,       /* nlink  */\n", raw_inode->nlink); +	fprintf(stdout, "        0x%02x,       /* spare  */\n", +			raw_inode->spare); +	fprintf(stdout, "        %u,          /* rename  */\n", +			raw_inode->rename); +	fprintf(stdout, "        %u,          /* deleted  */\n", +			raw_inode->deleted); +	fprintf(stdout, "        0x%02x,       /* accurate  */\n", +			raw_inode->accurate); +	fprintf(stdout, "        0x%08x, /* dchksum  */\n", raw_inode->dchksum); +	fprintf(stdout, "        0x%04x,     /* nchksum  */\n", raw_inode->nchksum); +	fprintf(stdout, "        0x%04x,     /* chksum  */\n", raw_inode->chksum); +	fprintf(stdout, "}\n"); +} + +static void write_val32(uint32_t *adr, uint32_t val) +{ +	switch(endian) { +		case ENDIAN_HOST: +			*adr = val; +			break; +		case ENDIAN_LITTLE: +			*adr = __cpu_to_le32(val); +			break; +		case ENDIAN_BIG: +			*adr = __cpu_to_be32(val); +			break; +	} +} + +static void write_val16(uint16_t *adr, uint16_t val) +{ +	switch(endian) { +		case ENDIAN_HOST: +			*adr = val; +			break; +		case ENDIAN_LITTLE: +			*adr = __cpu_to_le16(val); +			break; +		case ENDIAN_BIG: +			*adr = __cpu_to_be16(val); +			break; +	} +} + +static uint32_t read_val32(uint32_t *adr) +{ +	uint32_t val; + +	switch(endian) { +		case ENDIAN_HOST: +			val = *adr; +			break; +		case ENDIAN_LITTLE: +			val = __le32_to_cpu(*adr); +			break; +		case ENDIAN_BIG: +			val = __be32_to_cpu(*adr); +			break; +	} +	return val; +} + +static uint16_t read_val16(uint16_t *adr) +{ +	uint16_t val; + +	switch(endian) { +		case ENDIAN_HOST: +			val = *adr; +			break; +		case ENDIAN_LITTLE: +			val = __le16_to_cpu(*adr); +			break; +		case ENDIAN_BIG: +			val = __be16_to_cpu(*adr); +			break; +	} +	return val; +} + +	int +main(int argc, char **argv) +{ +	int fs; +	struct stat sb; +	uint32_t wordbuf; +	off_t pos = 0; +	off_t end; +	struct jffs_raw_inode ino; +	unsigned char namebuf[4096]; +	int myino = -1; + +	if (argc < 2) { +		printf("no filesystem given\n"); +		exit(1); +	} + +	fs = open(argv[1], O_RDONLY); +	if (fs < 0) { +		perror("open"); +		exit(1); +	} + +	if (argc > 2) { +		myino = atol(argv[2]); +		printf("Printing ino #%d\n" , myino); +	} + +	if (fstat(fs, &sb) < 0) { +		perror("stat"); +		close(fs); +		exit(1); +	} +	end = sb.st_size; + +	while (pos < end) { +		if (pread(fs, &wordbuf, 4, pos) < 0) { +			perror("pread"); +			exit(1); +		} + +		switch(wordbuf) { +			case JFFS_EMPTY_BITMASK: +				//			printf("0xff started at 0x%lx\n", pos); +				for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) { +					if (pread(fs, &wordbuf, 4, pos) < 0) { +						perror("pread"); +						exit(1); +					} +				} +				if (pos < end) +					pos -= 4; +				//			printf("0xff ended at 0x%lx\n", pos); +				continue; + +			case JFFS_DIRTY_BITMASK: +				//			printf("0x00 started at 0x%lx\n", pos); +				for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) { +					if (pread(fs, &wordbuf, 4, pos) < 0) { +						perror("pread"); +						exit(1); +					} +				} +				if (pos < end) +					pos -=4; +				//			printf("0x00 ended at 0x%lx\n", pos); +				continue; + +			default: +				printf("Argh. Dirty memory at 0x%lx\n", pos); +				//			file_hexdump(fs, pos, 128); +				for (pos += 4; pos < end; pos += 4) { +					if (pread(fs, &wordbuf, 4, pos) < 0) { +						perror("pread"); +						exit(1); +					} +					if (wordbuf == JFFS_MAGIC_BITMASK) +						break; +				} + +			case JFFS_MAGIC_BITMASK: +				if (pread(fs, &ino, sizeof(ino), pos) < 0) { +					perror("pread"); +					exit(1); +				} +				if (myino == -1 || ino.ino == myino) { +					printf("Magic found at 0x%lx\n", pos); +					jffs_print_raw_inode(&ino); +				} +				pos += sizeof(ino); + +				if (myino == -1 || ino.ino == myino) { +					if (ino.nsize) { +						if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) { +							perror("pread"); +							exit(1); +						} +						if (ino.nsize < 4095) +							namebuf[ino.nsize] = 0; +						else +							namebuf[4095] = 0; +						printf("Name: \"%s\"\n", namebuf); +					} else { +						printf("No Name\n"); +					} +				} +				pos += (ino.nsize + 3) & ~3; + +				pos += (ino.dsize + 3) & ~3; +		} + + + +	} +} diff --git a/jffsX-utils/jffs2dump.c b/jffsX-utils/jffs2dump.c new file mode 100644 index 0000000..f8b8ac7 --- /dev/null +++ b/jffsX-utils/jffs2dump.c @@ -0,0 +1,805 @@ +/* + *  dumpjffs2.c + * + *  Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + *   This utility dumps the contents of a binary JFFS2 image + * + * + * Bug/ToDo: + */ + +#define PROGRAM_NAME "jffs2dump" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <asm/types.h> +#include <dirent.h> +#include <mtd/jffs2-user.h> +#include <endian.h> +#include <byteswap.h> +#include <getopt.h> +#include <crc32.h> +#include "summary.h" +#include "common.h" + +#define PAD(x) (((x)+3)&~3) + +/* For outputting a byte-swapped version of the input image. */ +#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) +#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) + +#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) +#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) + +// Global variables +long	imglen;		// length of image +char	*data;		// image data + +void display_help (void) +{ +	printf("Usage: %s [OPTION]... INPUTFILE\n" +	       "Dump the contents of a binary JFFS2 image.\n\n" +	       "     --help                   display this help and exit\n" +	       "     --version                display version information and exit\n" +	       " -b, --bigendian              image is big endian\n" +	       " -l, --littleendian           image is little endian\n" +	       " -c, --content                dump image contents\n" +	       " -e, --endianconvert=FNAME    convert image endianness, output to file fname\n" +	       " -r, --recalccrc              recalc name and data crc on endian conversion\n" +	       " -d, --datsize=LEN            size of data chunks, when oob data in binary image (NAND only)\n" +	       " -o, --oobsize=LEN            size of oob data chunk in binary image (NAND only)\n" +	       " -v, --verbose                verbose output\n", +	       PROGRAM_NAME); +	exit(0); +} + +void display_version (void) +{ +	printf("%1$s " VERSION "\n" +			"\n" +			"Copyright (C) 2003 Thomas Gleixner \n" +			"\n" +			"%1$s comes with NO WARRANTY\n" +			"to the extent permitted by law.\n" +			"\n" +			"You may redistribute copies of %1$s\n" +			"under the terms of the GNU General Public Licence.\n" +			"See the file `COPYING' for more information.\n", +			PROGRAM_NAME); +	exit(0); +} + +// Option variables + +int 	verbose;		// verbose output +char 	*img;			// filename of image +int	dumpcontent;		// dump image content +int	target_endian = __BYTE_ORDER;	// image endianess +int	convertendian;		// convert endianness +int	recalccrc;		// recalc name and data crc's on endian conversion +char	cnvfile[256];		// filename for conversion output +int	datsize;		// Size of data chunks, when oob data is inside the binary image +int	oobsize;		// Size of oob chunks, when oob data is inside the binary image + +void process_options (int argc, char *argv[]) +{ +	int error = 0; + +	for (;;) { +		int option_index = 0; +		static const char *short_options = "blce:rd:o:v"; +		static const struct option long_options[] = { +			{"help", no_argument, 0, 0}, +			{"version", no_argument, 0, 0}, +			{"bigendian", no_argument, 0, 'b'}, +			{"littleendian", no_argument, 0, 'l'}, +			{"content", no_argument, 0, 'c'}, +			{"endianconvert", required_argument, 0, 'e'}, +			{"datsize", required_argument, 0, 'd'}, +			{"oobsize", required_argument, 0, 'o'}, +			{"recalccrc", required_argument, 0, 'r'}, +			{"verbose", no_argument, 0, 'v'}, +			{0, 0, 0, 0}, +		}; + +		int c = getopt_long(argc, argv, short_options, +				long_options, &option_index); +		if (c == EOF) { +			break; +		} + +		switch (c) { +			case 0: +				switch (option_index) { +					case 0: +						display_help(); +						break; +					case 1: +						display_version(); +						break; +				} +				break; +			case 'v': +				verbose = 1; +				break; +			case 'b': +				target_endian = __BIG_ENDIAN; +				break; +			case 'l': +				target_endian = __LITTLE_ENDIAN; +				break; +			case 'c': +				dumpcontent = 1; +				break; +			case 'd': +				datsize = atoi(optarg); +				break; +			case 'o': +				oobsize = atoi(optarg); +				break; +			case 'e': +				convertendian = 1; +				strcpy (cnvfile, optarg); +				break; +			case 'r': +				recalccrc = 1; +				break; +			case '?': +				error = 1; +				break; +		} +	} + +	if ((argc - optind) != 1 || error) +		display_help (); + +	img = argv[optind]; +} + + +/* + *	Dump image contents + */ +void do_dumpcontent (void) +{ +	char			*p = data, *p_free_begin; +	union jffs2_node_union 	*node; +	int			empty = 0, dirty = 0; +	char			name[256]; +	uint32_t		crc; +	uint16_t		type; +	int			bitchbitmask = 0; +	int			obsolete; + +	p_free_begin = NULL; +	while ( p < (data + imglen)) { +		node = (union jffs2_node_union*) p; + +		/* Skip empty space */ +		if (!p_free_begin) +			p_free_begin = p; +		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { +			p += 4; +			empty += 4; +			continue; +		} + +		if (p != p_free_begin) +			printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); +		p_free_begin = NULL; + +		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{ +			if (!bitchbitmask++) +				printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); +			p += 4; +			dirty += 4; +			continue; +		} +		bitchbitmask = 0; + +		type = je16_to_cpu(node->u.nodetype); +		if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { +			obsolete = 1; +			type |= JFFS2_NODE_ACCURATE; +		} else +			obsolete = 0; +		/* Set accurate for CRC check */ +		node->u.nodetype = cpu_to_je16(type); + +		crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); +		if (crc != je32_to_cpu (node->u.hdr_crc)) { +			printf ("Wrong hdr_crc  at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); +			p += 4; +			dirty += 4; +			continue; +		} + +		switch(je16_to_cpu(node->u.nodetype)) { + +			case JFFS2_NODETYPE_INODE: +				printf ("%8s Inode      node at 0x%08zx, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", +						obsolete ? "Obsolete" : "", +						p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), +						je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), +						je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); + +				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); +				if (crc != je32_to_cpu (node->i.node_crc)) { +					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); +					p += PAD(je32_to_cpu (node->i.totlen)); +					dirty += PAD(je32_to_cpu (node->i.totlen));; +					continue; +				} + +				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); +				if (crc != je32_to_cpu(node->i.data_crc)) { +					printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); +					p += PAD(je32_to_cpu (node->i.totlen)); +					dirty += PAD(je32_to_cpu (node->i.totlen));; +					continue; +				} + +				p += PAD(je32_to_cpu (node->i.totlen)); +				break; + +			case JFFS2_NODETYPE_DIRENT: +				memcpy (name, node->d.name, node->d.nsize); +				name [node->d.nsize] = 0x0; +				printf ("%8s Dirent     node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n", +						obsolete ? "Obsolete" : "", +						p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), +						je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), +						node->d.nsize, name); + +				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); +				if (crc != je32_to_cpu (node->d.node_crc)) { +					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); +					p += PAD(je32_to_cpu (node->d.totlen)); +					dirty += PAD(je32_to_cpu (node->d.totlen));; +					continue; +				} + +				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); +				if (crc != je32_to_cpu(node->d.name_crc)) { +					printf ("Wrong name_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); +					p += PAD(je32_to_cpu (node->d.totlen)); +					dirty += PAD(je32_to_cpu (node->d.totlen));; +					continue; +				} + +				p += PAD(je32_to_cpu (node->d.totlen)); +				break; + +			case JFFS2_NODETYPE_XATTR: +				memcpy(name, node->x.data, node->x.name_len); +				name[node->x.name_len] = '\x00'; +				printf ("%8s Xattr      node at 0x%08zx, totlen 0x%08x, xid   %5d, version %5d, name_len   %3d, name %s\n", +						obsolete ? "Obsolete" : "", +						p - data, +						je32_to_cpu (node->x.totlen), +						je32_to_cpu (node->x.xid), +						je32_to_cpu (node->x.version), +						node->x.name_len, +						name); + +				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc)); +				if (crc != je32_to_cpu (node->x.node_crc)) { +					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc); +					p += PAD(je32_to_cpu (node->x.totlen)); +					dirty += PAD(je32_to_cpu (node->x.totlen)); +					continue; +				} + +				crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1); +				if (crc != je32_to_cpu (node->x.data_crc)) { +					printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc); +					p += PAD(je32_to_cpu (node->x.totlen)); +					dirty += PAD(je32_to_cpu (node->x.totlen)); +					continue; +				} +				p += PAD(je32_to_cpu (node->x.totlen)); +				break; + +			case JFFS2_NODETYPE_XREF: +				printf ("%8s Xref       node at 0x%08zx, totlen 0x%08x, xid   %5d, xseqno  %5d, #ino  %8d\n", +						obsolete ? "Obsolete" : "", +						p - data, +						je32_to_cpu (node->r.totlen), +						je32_to_cpu (node->r.xid), +						je32_to_cpu (node->r.xseqno), +						je32_to_cpu (node->r.ino)); +				p += PAD(je32_to_cpu (node->r.totlen)); +				break; + +			case JFFS2_NODETYPE_SUMMARY: { + +											 int i; +											 struct jffs2_sum_marker * sm; + +											 printf("%8s Inode Sum  node at 0x%08zx, totlen 0x%08x, sum_num  %5d, cleanmarker size %5d\n", +													 obsolete ? "Obsolete" : "", +													 p - data, +													 je32_to_cpu (node->s.totlen), +													 je32_to_cpu (node->s.sum_num), +													 je32_to_cpu (node->s.cln_mkr)); + +											 crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); +											 if (crc != je32_to_cpu (node->s.node_crc)) { +												 printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); +												 p += PAD(je32_to_cpu (node->s.totlen)); +												 dirty += PAD(je32_to_cpu (node->s.totlen));; +												 continue; +											 } + +											 crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary),  je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); +											 if (crc != je32_to_cpu(node->s.sum_crc)) { +												 printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); +												 p += PAD(je32_to_cpu (node->s.totlen)); +												 dirty += PAD(je32_to_cpu (node->s.totlen));; +												 continue; +											 } + +											 if (verbose) { +												 void *sp; +												 sp = (p + sizeof(struct jffs2_raw_summary)); + +												 for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { + +													 switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { +														 case JFFS2_NODETYPE_INODE : { + +																						 struct jffs2_sum_inode_flash *spi; +																						 spi = sp; + +																						 printf ("%14s #ino  %5d,  version %5d, offset 0x%08x, totlen 0x%08x\n", +																								 "", +																								 je32_to_cpu (spi->inode), +																								 je32_to_cpu (spi->version), +																								 je32_to_cpu (spi->offset), +																								 je32_to_cpu (spi->totlen)); + +																						 sp += JFFS2_SUMMARY_INODE_SIZE; +																						 break; +																					 } + +														 case JFFS2_NODETYPE_DIRENT : { + +																						  char name[255]; +																						  struct jffs2_sum_dirent_flash *spd; +																						  spd = sp; + +																						  memcpy(name,spd->name,spd->nsize); +																						  name [spd->nsize] = 0x0; + +																						  printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino  %5d,  version %5d, #ino  %8d, nsize %8d, name %s \n", +																								  "", +																								  je32_to_cpu (spd->offset), +																								  je32_to_cpu (spd->totlen), +																								  je32_to_cpu (spd->pino), +																								  je32_to_cpu (spd->version), +																								  je32_to_cpu (spd->ino), +																								  spd->nsize, +																								  name); + +																						  sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); +																						  break; +																					  } + +														 case JFFS2_NODETYPE_XATTR : { +																						  struct jffs2_sum_xattr_flash *spx; +																						  spx = sp; +																						  printf ("%14s Xattr  offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n", +																								  "", +																								  je32_to_cpu (spx->offset), +																								  je32_to_cpu (spx->totlen), +																								  je32_to_cpu (spx->version), +																								  je32_to_cpu (spx->xid)); +																						  sp += JFFS2_SUMMARY_XATTR_SIZE; +																						  break; +																					  } + +														 case JFFS2_NODETYPE_XREF : { +																						  struct jffs2_sum_xref_flash *spr; +																						  spr = sp; +																						  printf ("%14s Xref   offset 0x%08x\n", +																								  "", +																								  je32_to_cpu (spr->offset)); +																						  sp += JFFS2_SUMMARY_XREF_SIZE; +																						  break; +																					  } + +														 default : +																					  printf("Unknown summary node!\n"); +																					  break; +													 } +												 } + +												 sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); + +												 printf("%14s Sum Node Offset  0x%08x, Magic 0x%08x, Padded size 0x%08x\n", +														 "", +														 je32_to_cpu(sm->offset), +														 je32_to_cpu(sm->magic), +														 je32_to_cpu(node->s.padded)); +											 } + +											 p += PAD(je32_to_cpu (node->s.totlen)); +											 break; +										 } + +			case JFFS2_NODETYPE_CLEANMARKER: +										 if (verbose) { +											 printf ("%8s Cleanmarker     at 0x%08zx, totlen 0x%08x\n", +													 obsolete ? "Obsolete" : "", +													 p - data, je32_to_cpu (node->u.totlen)); +										 } +										 p += PAD(je32_to_cpu (node->u.totlen)); +										 break; + +			case JFFS2_NODETYPE_PADDING: +										 if (verbose) { +											 printf ("%8s Padding    node at 0x%08zx, totlen 0x%08x\n", +													 obsolete ? "Obsolete" : "", +													 p - data, je32_to_cpu (node->u.totlen)); +										 } +										 p += PAD(je32_to_cpu (node->u.totlen)); +										 break; + +			case 0xffff: +										 p += 4; +										 empty += 4; +										 break; + +			default: +										 if (verbose) { +											 printf ("%8s Unknown    node at 0x%08zx, totlen 0x%08x\n", +													 obsolete ? "Obsolete" : "", +													 p - data, je32_to_cpu (node->u.totlen)); +										 } +										 p += PAD(je32_to_cpu (node->u.totlen)); +										 dirty += PAD(je32_to_cpu (node->u.totlen)); + +		} +	} + +	if (verbose) +		printf ("Empty space: %d, dirty space: %d\n", empty, dirty); +} + +/* + *	Convert endianess + */ +void do_endianconvert (void) +{ +	char			*p = data; +	union jffs2_node_union 	*node, newnode; +	int			fd, len; +	jint32_t		mode; +	uint32_t		crc; + +	fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); +	if (fd < 0) { +		fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); +		return; +	} + +	while ( p < (data + imglen)) { +		node = (union jffs2_node_union*) p; + +		/* Skip empty space */ +		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { +			write (fd, p, 4); +			p += 4; +			continue; +		} + +		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{ +			printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); +			newnode.u.magic = cnv_e16 (node->u.magic); +			newnode.u.nodetype = cnv_e16 (node->u.nodetype); +			write (fd, &newnode, 4); +			p += 4; +			continue; +		} + +		crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); +		if (crc != je32_to_cpu (node->u.hdr_crc)) { +			printf ("Wrong hdr_crc  at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); +		} + +		switch(je16_to_cpu(node->u.nodetype)) { + +			case JFFS2_NODETYPE_INODE: + +				newnode.i.magic = cnv_e16 (node->i.magic); +				newnode.i.nodetype = cnv_e16 (node->i.nodetype); +				newnode.i.totlen = cnv_e32 (node->i.totlen); +				newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); +				newnode.i.ino = cnv_e32 (node->i.ino); +				newnode.i.version = cnv_e32 (node->i.version); +				mode.v32 = node->i.mode.m; +				mode = cnv_e32 (mode); +				newnode.i.mode.m = mode.v32; +				newnode.i.uid = cnv_e16 (node->i.uid); +				newnode.i.gid = cnv_e16 (node->i.gid); +				newnode.i.isize = cnv_e32 (node->i.isize); +				newnode.i.atime = cnv_e32 (node->i.atime); +				newnode.i.mtime = cnv_e32 (node->i.mtime); +				newnode.i.ctime = cnv_e32 (node->i.ctime); +				newnode.i.offset = cnv_e32 (node->i.offset); +				newnode.i.csize = cnv_e32 (node->i.csize); +				newnode.i.dsize = cnv_e32 (node->i.dsize); +				newnode.i.compr = node->i.compr; +				newnode.i.usercompr = node->i.usercompr; +				newnode.i.flags = cnv_e16 (node->i.flags); +				if (recalccrc) { +					len = je32_to_cpu(node->i.csize); +					newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); +				} else +					newnode.i.data_crc = cnv_e32 (node->i.data_crc); + +				newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); + +				write (fd, &newnode, sizeof (struct jffs2_raw_inode)); +				write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) -  sizeof (struct jffs2_raw_inode))); + +				p += PAD(je32_to_cpu (node->i.totlen)); +				break; + +			case JFFS2_NODETYPE_DIRENT: +				newnode.d.magic = cnv_e16 (node->d.magic); +				newnode.d.nodetype = cnv_e16 (node->d.nodetype); +				newnode.d.totlen = cnv_e32 (node->d.totlen); +				newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); +				newnode.d.pino = cnv_e32 (node->d.pino); +				newnode.d.version = cnv_e32 (node->d.version); +				newnode.d.ino = cnv_e32 (node->d.ino); +				newnode.d.mctime = cnv_e32 (node->d.mctime); +				newnode.d.nsize = node->d.nsize; +				newnode.d.type = node->d.type; +				newnode.d.unused[0] = node->d.unused[0]; +				newnode.d.unused[1] = node->d.unused[1]; +				newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); +				if (recalccrc) +					newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); +				else +					newnode.d.name_crc = cnv_e32 (node->d.name_crc); + +				write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); +				write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) -  sizeof (struct jffs2_raw_dirent))); +				p += PAD(je32_to_cpu (node->d.totlen)); +				break; + +			case JFFS2_NODETYPE_XATTR: +				newnode.x.magic = cnv_e16 (node->x.magic); +				newnode.x.nodetype = cnv_e16 (node->x.nodetype); +				newnode.x.totlen = cnv_e32 (node->x.totlen); +				newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); +				newnode.x.xid = cnv_e32 (node->x.xid); +				newnode.x.version = cnv_e32 (node->x.version); +				newnode.x.xprefix = node->x.xprefix; +				newnode.x.name_len = node->x.name_len; +				newnode.x.value_len = cnv_e16 (node->x.value_len); +				if (recalccrc) +					newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1)); +				else +					newnode.x.data_crc = cnv_e32 (node->x.data_crc); +				newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc))); + +				write (fd, &newnode, sizeof (struct jffs2_raw_xattr)); +				write (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) -  sizeof (struct jffs2_raw_xattr))); +				p += PAD(je32_to_cpu (node->x.totlen)); +				break; + +			case JFFS2_NODETYPE_XREF: +				newnode.r.magic = cnv_e16 (node->r.magic); +				newnode.r.nodetype = cnv_e16 (node->r.nodetype); +				newnode.r.totlen = cnv_e32 (node->r.totlen); +				newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc))); +				newnode.r.ino = cnv_e32 (node->r.ino); +				newnode.r.xid = cnv_e32 (node->r.xid); +				newnode.r.xseqno = cnv_e32 (node->r.xseqno); +				newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc))); +				p += PAD(je32_to_cpu (node->x.totlen)); +				break; + +			case JFFS2_NODETYPE_CLEANMARKER: +			case JFFS2_NODETYPE_PADDING: +				newnode.u.magic = cnv_e16 (node->u.magic); +				newnode.u.nodetype = cnv_e16 (node->u.nodetype); +				newnode.u.totlen = cnv_e32 (node->u.totlen); +				newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + +				write (fd, &newnode, sizeof (struct jffs2_unknown_node)); +				len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); +				if (len > 0) +					write (fd, p + sizeof (struct jffs2_unknown_node), len); + +				p += PAD(je32_to_cpu (node->u.totlen)); +				break; + +			case JFFS2_NODETYPE_SUMMARY : { +											  struct jffs2_sum_marker *sm_ptr; +											  int i,sum_len; +											  int counter = 0; + +											  newnode.s.magic = cnv_e16 (node->s.magic); +											  newnode.s.nodetype = cnv_e16 (node->s.nodetype); +											  newnode.s.totlen = cnv_e32 (node->s.totlen); +											  newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); +											  newnode.s.sum_num = cnv_e32 (node->s.sum_num); +											  newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); +											  newnode.s.padded = cnv_e32 (node->s.padded); + +											  newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); + +											  // summary header +											  p += sizeof (struct jffs2_raw_summary); + +											  // summary data +											  sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); + +											  for (i=0; i<je32_to_cpu (node->s.sum_num); i++) { +												  union jffs2_sum_flash *fl_ptr; + +												  fl_ptr = (union jffs2_sum_flash *) p; + +												  switch (je16_to_cpu (fl_ptr->u.nodetype)) { +													  case JFFS2_NODETYPE_INODE: + +														  fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); +														  fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); +														  fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); +														  fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); +														  fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); +														  p += sizeof (struct jffs2_sum_inode_flash); +														  counter += sizeof (struct jffs2_sum_inode_flash); +														  break; + +													  case JFFS2_NODETYPE_DIRENT: +														  fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); +														  fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); +														  fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); +														  fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); +														  fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); +														  fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); +														  p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; +														  counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; +														  break; + +													  case JFFS2_NODETYPE_XATTR: +														  fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype); +														  fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid); +														  fl_ptr->x.version = cnv_e32 (fl_ptr->x.version); +														  fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset); +														  fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen); +														  p += sizeof (struct jffs2_sum_xattr_flash); +														  counter += sizeof (struct jffs2_sum_xattr_flash); +														  break; + +													  case JFFS2_NODETYPE_XREF: +														  fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype); +														  fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset); +														  p += sizeof (struct jffs2_sum_xref_flash); +														  counter += sizeof (struct jffs2_sum_xref_flash); +														  break; + +													  default : +														  printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); +														  exit(EXIT_FAILURE); +														  break; +												  } + +											  } + +											  //pad +											  p += sum_len - counter; + +											  // summary marker +											  sm_ptr = (struct jffs2_sum_marker *) p; +											  sm_ptr->offset = cnv_e32 (sm_ptr->offset); +											  sm_ptr->magic = cnv_e32 (sm_ptr->magic); +											  p += sizeof (struct jffs2_sum_marker); + +											  // generate new crc on sum data +											  newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), +														  je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); + +											  // write out new node header +											  write(fd, &newnode, sizeof (struct jffs2_raw_summary)); +											  // write out new summary data +											  write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); + +											  break; +										  } + +			case 0xffff: +										  write (fd, p, 4); +										  p += 4; +										  break; + +			default: +										  printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); +										  p += PAD(je32_to_cpu (node->u.totlen)); + +		} +	} + +	close (fd); + +} + +/* + * Main program + */ +int main(int argc, char **argv) +{ +	int fd; + +	process_options(argc, argv); + +	/* Open the input file */ +	if ((fd = open(img, O_RDONLY)) == -1) { +		perror("open input file"); +		exit(1); +	} + +	// get image length +	imglen = lseek(fd, 0, SEEK_END); +	lseek (fd, 0, SEEK_SET); + +	data = malloc (imglen); +	if (!data) { +		perror("out of memory"); +		close (fd); +		exit(1); +	} + +	if (datsize && oobsize) { +		int  idx = 0; +		long len = imglen; +		uint8_t oob[oobsize]; +		printf ("Peeling data out of combined data/oob image\n"); +		while (len) { +			// read image data +			read (fd, &data[idx], datsize); +			read (fd, oob, oobsize); +			idx += datsize; +			imglen -= oobsize; +			len -= datsize + oobsize; +		} + +	} else { +		// read image data +		read (fd, data, imglen); +	} +	// Close the input file +	close(fd); + +	if (dumpcontent) +		do_dumpcontent (); + +	if (convertendian) +		do_endianconvert (); + +	// free memory +	free (data); + +	// Return happy +	exit (0); +} diff --git a/jffsX-utils/jffs2reader.c b/jffsX-utils/jffs2reader.c new file mode 100644 index 0000000..a62da9a --- /dev/null +++ b/jffsX-utils/jffs2reader.c @@ -0,0 +1,918 @@ +/* vi: set sw=4 ts=4: */ +/* + * jffs2reader v0.0.18 A jffs2 image reader + * + * Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * + ********* + *  This code was altered September 2001 + *  Changes are Copyright (c) Erik Andersen <andersen@codepoet.org> + * + * In compliance with (2) above, this is hereby marked as an altered + * version of this software.  It has been altered as follows: + *      *) Listing a directory now mimics the behavior of 'ls -l' + *      *) Support for recursive listing has been added + *      *) Without options, does a recursive 'ls' on the whole filesystem + *      *) option parsing now uses getopt() + *      *) Now uses printf, and error messages go to stderr. + *      *) The copyright notice has been cleaned up and reformatted + *      *) The code has been reformatted + *      *) Several twisty code paths have been fixed so I can understand them. + *  -Erik, 1 September 2001 + * + *      *) Made it show major/minor numbers for device nodes + *      *) Made it show symlink targets + *  -Erik, 13 September 2001 + */ + + +/* +TODO: + +- Add CRC checking code to places marked with XXX. +- Add support for other node compression types. + +- Test with real life images. +- Maybe port into bootloader. + */ + +/* +BUGS: + +- Doesn't check CRC checksums. + */ + +#define PROGRAM_NAME "jffs2reader" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <zlib.h> + +#include "mtd/jffs2-user.h" +#include "common.h" + +#define SCRATCH_SIZE (5*1024*1024) + +/* macro to avoid "lvalue required as left operand of assignment" error */ +#define ADD_BYTES(p, n)		((p) = (typeof(p))((char *)(p) + (n))) + +#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) +#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) + +struct dir { +	struct dir *next; +	uint8_t type; +	uint8_t nsize; +	uint32_t ino; +	char name[256]; +}; + +int target_endian = __BYTE_ORDER; + +void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *); +struct dir *putdir(struct dir *, struct jffs2_raw_dirent *); +void printdir(char *o, size_t size, struct dir *d, const char *path, +		int recurse, int want_ctime); +void freedir(struct dir *); + +struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino); +struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t, +		char *, uint8_t); +struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t); +struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t); + +struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, const char *, +		uint32_t *, int); +struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, +		uint32_t *); + +void lsdir(char *, size_t, const char *, int, int); +void catfile(char *, size_t, char *, char *, size_t, size_t *); + +int main(int, char **); + +/* writes file node into buffer, to the proper position. */ +/* reading all valid nodes in version order reconstructs the file. */ + +/* +   b       - buffer +   bsize   - buffer size +   rsize   - result size +   n       - node + */ + +void putblock(char *b, size_t bsize, size_t * rsize, +		struct jffs2_raw_inode *n) +{ +	uLongf dlen = je32_to_cpu(n->dsize); + +	if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) +		errmsg_die("File does not fit into buffer!"); + +	if (*rsize < je32_to_cpu(n->isize)) +		bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); + +	switch (n->compr) { +		case JFFS2_COMPR_ZLIB: +			uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, +					(Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), +					(uLongf) je32_to_cpu(n->csize)); +			break; + +		case JFFS2_COMPR_NONE: +			memcpy(b + je32_to_cpu(n->offset), +					((char *) n) + sizeof(struct jffs2_raw_inode), dlen); +			break; + +		case JFFS2_COMPR_ZERO: +			bzero(b + je32_to_cpu(n->offset), dlen); +			break; + +			/* [DYN]RUBIN support required! */ + +		default: +			errmsg_die("Unsupported compression method!"); +	} + +	*rsize = je32_to_cpu(n->isize); +} + +/* adds/removes directory node into dir struct. */ +/* reading all valid nodes in version order reconstructs the directory. */ + +/* +   dd      - directory struct being processed +   n       - node + +   return value: directory struct value replacing dd + */ + +struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n) +{ +	struct dir *o, *d, *p; + +	o = dd; + +	if (je32_to_cpu(n->ino)) { +		if (dd == NULL) { +			d = xmalloc(sizeof(struct dir)); +			d->type = n->type; +			memcpy(d->name, n->name, n->nsize); +			d->nsize = n->nsize; +			d->ino = je32_to_cpu(n->ino); +			d->next = NULL; + +			return d; +		} + +		while (1) { +			if (n->nsize == dd->nsize && +					!memcmp(n->name, dd->name, n->nsize)) { +				dd->type = n->type; +				dd->ino = je32_to_cpu(n->ino); + +				return o; +			} + +			if (dd->next == NULL) { +				dd->next = xmalloc(sizeof(struct dir)); +				dd->next->type = n->type; +				memcpy(dd->next->name, n->name, n->nsize); +				dd->next->nsize = n->nsize; +				dd->next->ino = je32_to_cpu(n->ino); +				dd->next->next = NULL; + +				return o; +			} + +			dd = dd->next; +		} +	} else { +		if (dd == NULL) +			return NULL; + +		if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { +			d = dd->next; +			free(dd); +			return d; +		} + +		while (1) { +			p = dd; +			dd = dd->next; + +			if (dd == NULL) +				return o; + +			if (n->nsize == dd->nsize && +					!memcmp(n->name, dd->name, n->nsize)) { +				p->next = dd->next; +				free(dd); + +				return o; +			} +		} +	} +} + + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) + +/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ +static const mode_t SBIT[] = { +	0, 0, S_ISUID, +	0, 0, S_ISGID, +	0, 0, S_ISVTX +}; + +/* The 9 mode bits to test */ +static const mode_t MBIT[] = { +	S_IRUSR, S_IWUSR, S_IXUSR, +	S_IRGRP, S_IWGRP, S_IXGRP, +	S_IROTH, S_IWOTH, S_IXOTH +}; + +static const char MODE1[] = "rwxrwxrwx"; +static const char MODE0[] = "---------"; +static const char SMODE1[] = "..s..s..t"; +static const char SMODE0[] = "..S..S..T"; + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char *mode_string(int mode) +{ +	static char buf[12]; + +	int i; + +	buf[0] = TYPECHAR(mode); +	for (i = 0; i < 9; i++) { +		if (mode & SBIT[i]) +			buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i]; +		else +			buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i]; +	} +	return buf; +} + +/* prints contents of directory structure */ + +/* +   d       - dir struct + */ + +void printdir(char *o, size_t size, struct dir *d, const char *path, int recurse, +		int want_ctime) +{ +	char m; +	char *filetime; +	time_t age; +	struct jffs2_raw_inode *ri; +	jint32_t mode; + +	if (!path) +		return; +	if (strlen(path) == 1 && *path == '/') +		path++; + +	while (d != NULL) { +		switch (d->type) { +			case DT_REG: +				m = ' '; +				break; + +			case DT_FIFO: +				m = '|'; +				break; + +			case DT_CHR: +				m = ' '; +				break; + +			case DT_BLK: +				m = ' '; +				break; + +			case DT_DIR: +				m = '/'; +				break; + +			case DT_LNK: +				m = ' '; +				break; + +			case DT_SOCK: +				m = '='; +				break; + +			default: +				m = '?'; +		} +		ri = find_raw_inode(o, size, d->ino); +		if (!ri) { +			warnmsg("bug: raw_inode missing!"); +			d = d->next; +			continue; +		} + +		filetime = ctime((const time_t *) &(ri->ctime)); +		age = time(NULL) - je32_to_cpu(ri->ctime); +		mode.v32 = ri->mode.m; +		printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), +				1, je16_to_cpu(ri->uid), je16_to_cpu(ri->gid)); +		if ( d->type==DT_BLK || d->type==DT_CHR ) { +			dev_t rdev; +			size_t devsize; +			putblock((char*)&rdev, sizeof(rdev), &devsize, ri); +			printf("%4d, %3d ", major(rdev), minor(rdev)); +		} else { +			printf("%9ld ", (long)je32_to_cpu(ri->dsize)); +		} +		d->name[d->nsize]='\0'; +		if (want_ctime) { +			if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) +				/* hh:mm if less than 6 months old */ +				printf("%6.6s %5.5s ", filetime + 4, filetime + 11); +			else +				printf("%6.6s %4.4s ", filetime + 4, filetime + 20); +		} +		printf("%s/%s%c", path, d->name, m); +		if (d->type == DT_LNK) { +			char symbuf[1024]; +			size_t symsize; +			putblock(symbuf, sizeof(symbuf), &symsize, ri); +			symbuf[symsize] = 0; +			printf(" -> %s", symbuf); +		} +		printf("\n"); + +		if (d->type == DT_DIR && recurse) { +			char *tmp; +			tmp = xmalloc(BUFSIZ); +			sprintf(tmp, "%s/%s", path, d->name); +			lsdir(o, size, tmp, recurse, want_ctime);	/* Go recursive */ +			free(tmp); +		} + +		d = d->next; +	} +} + +/* frees memory used by directory structure */ + +/* +   d       - dir struct + */ + +void freedir(struct dir *d) +{ +	struct dir *t; + +	while (d != NULL) { +		t = d->next; +		free(d); +		d = t; +	} +} + +/* collects directory/file nodes in version order. */ + +/* +   f       - file flag. +   if zero, collect file, compare ino to inode +   otherwise, collect directory, compare ino to parent inode +   o       - filesystem image pointer +   size    - size of filesystem image +   ino     - inode to compare against. see f. + +   return value: a jffs2_raw_inode that corresponds the the specified +   inode, or NULL + */ + +struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino) +{ +	/* aligned! */ +	union jffs2_node_union *n; +	union jffs2_node_union *e = (union jffs2_node_union *) (o + size); +	union jffs2_node_union *lr;	/* last block position */ +	union jffs2_node_union *mp = NULL;	/* minimum position */ + +	uint32_t vmin, vmint, vmaxt, vmax, vcur, v; + +	vmin = 0;					/* next to read */ +	vmax = ~((uint32_t) 0);		/* last to read */ +	vmint = ~((uint32_t) 0); +	vmaxt = 0;					/* found maximum */ +	vcur = 0;					/* XXX what is smallest version number used? */ +	/* too low version number can easily result excess log rereading */ + +	n = (union jffs2_node_union *) o; +	lr = n; + +	do { +		while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) +			ADD_BYTES(n, 4); + +		if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { +			if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && +				je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(n->i.version)) > vcur) { +				/* XXX crc check */ + +				if (vmaxt < v) +					vmaxt = v; +				if (vmint > v) { +					vmint = v; +					mp = n; +				} + +				if (v == (vcur + 1)) +					return (&(n->i)); +			} + +			ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); +		} else +			n = (union jffs2_node_union *) o;	/* we're at the end, rewind to the beginning */ + +		if (lr == n) {			/* whole loop since last read */ +			vmax = vmaxt; +			vmin = vmint; +			vmint = ~((uint32_t) 0); + +			if (vcur < vmax && vcur < vmin) +				return (&(mp->i)); +		} +	} while (vcur < vmax); + +	return NULL; +} + +/* collects dir struct for selected inode */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   pino    - inode of the specified directory +   d       - input directory structure + +   return value: result directory structure, replaces d. + */ + +struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d) +{ +	/* aligned! */ +	union jffs2_node_union *n; +	union jffs2_node_union *e = (union jffs2_node_union *) (o + size); +	union jffs2_node_union *lr;	/* last block position */ +	union jffs2_node_union *mp = NULL;	/* minimum position */ + +	uint32_t vmin, vmint, vmaxt, vmax, vcur, v; + +	vmin = 0;					/* next to read */ +	vmax = ~((uint32_t) 0);		/* last to read */ +	vmint = ~((uint32_t) 0); +	vmaxt = 0;					/* found maximum */ +	vcur = 0;					/* XXX what is smallest version number used? */ +	/* too low version number can easily result excess log rereading */ + +	n = (union jffs2_node_union *) o; +	lr = n; + +	do { +		while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) +			ADD_BYTES(n, 4); + +		if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { +			if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && +				je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(n->d.version)) > vcur) { +				/* XXX crc check */ + +				if (vmaxt < v) +					vmaxt = v; +				if (vmint > v) { +					vmint = v; +					mp = n; +				} + +				if (v == (vcur + 1)) { +					d = putdir(d, &(n->d)); + +					lr = n; +					vcur++; +					vmint = ~((uint32_t) 0); +				} +			} + +			ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); +		} else +			n = (union jffs2_node_union *) o;	/* we're at the end, rewind to the beginning */ + +		if (lr == n) {			/* whole loop since last read */ +			vmax = vmaxt; +			vmin = vmint; +			vmint = ~((uint32_t) 0); + +			if (vcur < vmax && vcur < vmin) { +				d = putdir(d, &(mp->d)); + +				lr = n = +					(union jffs2_node_union *) (((char *) mp) + +							((je32_to_cpu(mp->u.totlen) + 3) & ~3)); + +				vcur = vmin; +			} +		} +	} while (vcur < vmax); + +	return d; +} + + + +/* resolve dirent based on criteria */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   ino     - if zero, ignore, +   otherwise compare against dirent inode +   pino    - if zero, ingore, +   otherwise compare against parent inode +   and use name and nsize as extra criteria +   name    - name of wanted dirent, used if pino!=0 +   nsize   - length of name of wanted dirent, used if pino!=0 + +   return value: pointer to relevant dirent structure in +   filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolvedirent(char *o, size_t size, +		uint32_t ino, uint32_t pino, +		char *name, uint8_t nsize) +{ +	/* aligned! */ +	union jffs2_node_union *n; +	union jffs2_node_union *e = (union jffs2_node_union *) (o + size); + +	struct jffs2_raw_dirent *dd = NULL; + +	uint32_t vmax, v; + +	if (!pino && ino <= 1) +		return dd; + +	vmax = 0; + +	n = (union jffs2_node_union *) o; + +	do { +		while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) +			ADD_BYTES(n, 4); + +		if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { +			if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && +					(!ino || je32_to_cpu(n->d.ino) == ino) && +					(v = je32_to_cpu(n->d.version)) > vmax && +					(!pino || (je32_to_cpu(n->d.pino) == pino && +							   nsize == n->d.nsize && +							   !memcmp(name, n->d.name, nsize)))) { +				/* XXX crc check */ + +				if (vmax < v) { +					vmax = v; +					dd = &(n->d); +				} +			} + +			ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); +		} else +			return dd; +	} while (1); +} + +/* resolve name under certain parent inode to dirent */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   pino    - requested parent inode +   name    - name of wanted dirent +   nsize   - length of name of wanted dirent + +   return value: pointer to relevant dirent structure in +   filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino, +		char *name, uint8_t nsize) +{ +	return resolvedirent(o, size, 0, pino, name, nsize); +} + +/* resolve inode to dirent */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   ino     - compare against dirent inode + +   return value: pointer to relevant dirent structure in +   filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino) +{ +	return resolvedirent(o, size, ino, 0, NULL, 0); +} + +/* resolve slash-style path into dirent and inode. +   slash as first byte marks absolute path (root=inode 1). +   . and .. are resolved properly, and symlinks are followed. + */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   ino     - root inode, used if path is relative +   p       - path to be resolved +   inos    - result inode, zero if failure +   recc    - recursion count, to detect symlink loops + +   return value: pointer to dirent struct in file system image. +   note that root directory doesn't have dirent struct +   (return value is NULL), but it has inode (*inos=1) + */ + +struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino, +		const char *p, uint32_t * inos, int recc) +{ +	struct jffs2_raw_dirent *dir = NULL; + +	int d = 1; +	uint32_t tino; + +	char *next; + +	char *path, *pp; + +	char symbuf[1024]; +	size_t symsize; + +	if (recc > 16) { +		/* probably symlink loop */ +		*inos = 0; +		return NULL; +	} + +	pp = path = xstrdup(p); + +	if (*path == '/') { +		path++; +		ino = 1; +	} + +	if (ino > 1) { +		dir = resolveinode(o, size, ino); + +		ino = DIRENT_INO(dir); +	} + +	next = path - 1; + +	while (ino && next != NULL && next[1] != 0 && d) { +		path = next + 1; +		next = strchr(path, '/'); + +		if (next != NULL) +			*next = 0; + +		if (*path == '.' && path[1] == 0) +			continue; +		if (*path == '.' && path[1] == '.' && path[2] == 0) { +			if (DIRENT_PINO(dir) == 1) { +				ino = 1; +				dir = NULL; +			} else { +				dir = resolveinode(o, size, DIRENT_PINO(dir)); +				ino = DIRENT_INO(dir); +			} + +			continue; +		} + +		dir = resolvename(o, size, ino, path, (uint8_t) strlen(path)); + +		if (DIRENT_INO(dir) == 0 || +				(next != NULL && +				 !(dir->type == DT_DIR || dir->type == DT_LNK))) { +			free(pp); + +			*inos = 0; + +			return NULL; +		} + +		if (dir->type == DT_LNK) { +			struct jffs2_raw_inode *ri; +			ri = find_raw_inode(o, size, DIRENT_INO(dir)); +			putblock(symbuf, sizeof(symbuf), &symsize, ri); +			symbuf[symsize] = 0; + +			tino = ino; +			ino = 0; + +			dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc); + +			if (dir != NULL && next != NULL && +					!(dir->type == DT_DIR || dir->type == DT_LNK)) { +				free(pp); + +				*inos = 0; +				return NULL; +			} +		} +		if (dir != NULL) +			ino = DIRENT_INO(dir); +	} + +	free(pp); + +	*inos = ino; + +	return dir; +} + +/* resolve slash-style path into dirent and inode. +   slash as first byte marks absolute path (root=inode 1). +   . and .. are resolved properly, and symlinks are followed. + */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   ino     - root inode, used if path is relative +   p       - path to be resolved +   inos    - result inode, zero if failure + +   return value: pointer to dirent struct in file system image. +   note that root directory doesn't have dirent struct +   (return value is NULL), but it has inode (*inos=1) + */ + +struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino, +		const char *p, uint32_t * inos) +{ +	return resolvepath0(o, size, ino, p, inos, 0); +} + +/* lists files on directory specified by path */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   p       - path to be resolved + */ + +void lsdir(char *o, size_t size, const char *path, int recurse, int want_ctime) +{ +	struct jffs2_raw_dirent *dd; +	struct dir *d = NULL; + +	uint32_t ino; + +	dd = resolvepath(o, size, 1, path, &ino); + +	if (ino == 0 || +			(dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) +		errmsg_die("%s: No such file or directory", path); + +	d = collectdir(o, size, ino, d); +	printdir(o, size, d, path, recurse, want_ctime); +	freedir(d); +} + +/* writes file specified by path to the buffer */ + +/* +   o       - filesystem image pointer +   size    - size of filesystem image +   p       - path to be resolved +   b       - file buffer +   bsize   - file buffer size +   rsize   - file result size + */ + +void catfile(char *o, size_t size, char *path, char *b, size_t bsize, +		size_t * rsize) +{ +	struct jffs2_raw_dirent *dd; +	struct jffs2_raw_inode *ri; +	uint32_t ino; + +	dd = resolvepath(o, size, 1, path, &ino); + +	if (ino == 0) +		errmsg_die("%s: No such file or directory", path); + +	if (dd == NULL || dd->type != DT_REG) +		errmsg_die("%s: Not a regular file", path); + +	ri = find_raw_inode(o, size, ino); +	putblock(b, bsize, rsize, ri); + +	write(1, b, *rsize); +} + +/* usage example */ + +int main(int argc, char **argv) +{ +	int fd, opt, recurse = 0, want_ctime = 0; +	struct stat st; + +	char *scratch, *dir = NULL, *file = NULL; +	size_t ssize = 0; + +	char *buf; + +	while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { +		switch (opt) { +			case 'd': +				dir = optarg; +				break; +			case 'f': +				file = optarg; +				break; +			case 'r': +				recurse++; +				break; +			case 't': +				want_ctime++; +				break; +			default: +				fprintf(stderr, +						"Usage: %s <image> [-d|-f] < path >\n", +						PROGRAM_NAME); +				exit(EXIT_FAILURE); +		} +	} + +	fd = open(argv[optind], O_RDONLY); +	if (fd == -1) +		sys_errmsg_die("%s", argv[optind]); + +	if (fstat(fd, &st)) +		sys_errmsg_die("%s", argv[optind]); + +	buf = xmalloc((size_t) st.st_size); + +	if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) +		sys_errmsg_die("%s", argv[optind]); + +	if (dir) +		lsdir(buf, st.st_size, dir, recurse, want_ctime); + +	if (file) { +		scratch = xmalloc(SCRATCH_SIZE); + +		catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); +		free(scratch); +	} + +	if (!dir && !file) +		lsdir(buf, st.st_size, "/", 1, want_ctime); + + +	free(buf); +	exit(EXIT_SUCCESS); +} diff --git a/jffsX-utils/mkfs.jffs2.1 b/jffsX-utils/mkfs.jffs2.1 new file mode 100644 index 0000000..7c57ddc --- /dev/null +++ b/jffsX-utils/mkfs.jffs2.1 @@ -0,0 +1,268 @@ +.TH MKFS.JFFS2 1 +.SH NAME +mkfs.jffs2 \- Create a JFFS2 file system image from directory +.SH SYNOPSIS +.B mkfs.jffs2 +[ +.B -p,--pad[=SIZE] +] +[ +.B -r,-d,--root +.I directory +] +[ +.B -s,--pagesize=SIZE +] +[ +.B -e,--eraseblock=SIZE +] +[ +.B -c,--cleanmarker=SIZE +] +[ +.B -n,--no-cleanmarkers +] +[ +.B -o,--output +.I image.jffs2 +] +[ +.B -l,--little-endian +] +[ +.B -b,--big-endian +] +[ +.B -D,--devtable=FILE +] +[ +.B -f,--faketime +] +[ +.B -q,--squash +] +[ +.B -U,--squash-uids +] +[ +.B -P,--squash-perms +] +[ +.B --with-xattr +] +[ +.B --with-selinux +] +[ +.B --with-posix-acl +] +[ +.B -m,--compression-mode=MODE +] +[ +.B -x,--disable-compressor=NAME +] +[ +.B -X,--enable-compressor=NAME +] +[ +.B -y,--compressor-priority=PRIORITY:NAME +] +[ +.B -L,--list-compressors +] +[ +.B -t,--test-compression +] +[ +.B -h,--help +] +[ +.B -v,--verbose +] +[ +.B -V,--version +] +[ +.B -i,--incremental +.I image.jffs2 +] + +.SH DESCRIPTION +The program +.B mkfs.jffs2 +creates a JFFS2 (Second Journalling Flash File System) file system +image and writes the resulting image to the file specified by the +.B -o +option or by default to the standard output, unless the standard +output is a terminal device in which case mkfs.jffs2 will abort. + +The file system image is created using the files and directories +contained in the directory specified by the option +.B -r +or the present directory, if the +.B -r +option is not specified. + +Each block of the files to be placed into the file system image +are compressed using one of the available compressors depending +on the selected compression mode. + +File systems are created with the same endianness as the host, +unless the +.B -b +or +.B -l +options are specified.  JFFS2 driver in the 2.4 Linux kernel only +supported images having the same endianness as the CPU. As of 2.5.48, +the kernel can be changed with a #define to accept images of the +non-native endianness. Full bi-endian support in the kernel is not +planned. + +It is unlikely that JFFS2 images are useful except in conjuction +with the MTD (Memory Technology Device) drivers in the Linux +kernel, since the JFFS2 file system driver in the kernel requires +MTD devices. +.SH OPTIONS +Options that take SIZE arguments can be specified as either +decimal (e.g., 65536), octal (0200000), or hexidecimal (0x1000). +.TP +.B -p, --pad[=SIZE] +Pad output to SIZE bytes with 0xFF.  If SIZE is not specified, +the output is padded to the end of the final erase block. +.TP +.B -r, -d, --root=DIR +Build file system from directory DIR.  The default is the current +directory. +.TP +.B -s, --pagesize=SIZE +Use page size SIZE.  The default is 4 KiB.  This size is the +maximum size of a data node.  Set according to target system's memory +management page size (NOTE: this is NOT related to NAND page size). +.TP +.B -e, --eraseblock=SIZE +Use erase block size SIZE.  The default is 64 KiB.  If you use a erase +block size different than the erase block size of the target MTD +device, JFFS2 may not perform optimally. If the SIZE specified is +below 4096, the units are assumed to be KiB. +.TP +.B -c, --cleanmarker=SIZE +Write \'CLEANMARKER\' nodes with the size specified. It is not +normally appropriate to specify a size other than the default 12 +bytes. +.TP +.B -n, --no-cleanmarkers +Do not write \'CLEANMARKER\' nodes to the beginning of each erase +block. This option can be useful for creating JFFS2 images for +use on NAND flash, and for creating images which are to be used +on a variety of hardware with differing eraseblock sizes. +.TP +.B -o, --output=FILE +Write JFFS2 image to file FILE.  Default is the standard output. +.TP +.B -l, --little-endian +Create a little-endian JFFS2 image.  Default is to make an image +with the same endianness as the host. +.TP +.B -b, --big-endian +Create a big-endian JFFS2 image.  Default is to make an image +with the same endianness as the host. +.TP +.B -D, --devtable=FILE +Use the named FILE as a device table file, for including devices and +changing permissions in the created image when the user does not have +appropriate permissions to create them on the file system used as +source. +.TP +.B -f, --faketime +Change all file timestamps to \'0\' for regression testing. +.TP +.B -q, --squash +Squash permissions and owners, making all files be owned by root and +removing write permission for \'group\' and \'other\'. +.TP +.B -U, --squash-uids +Squash owners making all files be owned by root. +.TP +.B -P, --squash-perms +Squash permissions, removing write permission for \'group\' and \'other\'. +.TP +.B --with-xattr +Enables xattr, stuff all xattr entries into jffs2 image file. +.TP +.B --with-selinux +Enables xattr, stuff only SELinux Labels into jffs2 image file. +.TP +.B --with-posix-acl +Enable xattr, stuff only POSIX ACL entries into jffs2 image file. +.TP +.B -m, --compression-mode=MODE +Set the default compression mode. The default mode is +.B priority +which tries the compressors in a predefinied order and chooses the first +successful one. The alternatives are: +.B none +(mkfs will not compress) and +.B size +(mkfs will try all compressor and chooses the one which have the smallest result). +.TP +.B -x, --disable-compressor=NAME +Disable a compressor. Use +.B -L +to see the list of the available compressors and their default states. +.TP +.B -X, --enable-compressor=NAME +Enable a compressor. Use +.B -L +to see the list of the available compressors and their default states. +.TP +.B -y, --compressor-priority=PRIORITY:NAME +Set the priority of a compressor. Use +.B -L +to see the list of the available compressors and their default priority. +Priorities are used by priority compression mode. +.TP +.B -L, --list-compressors +Show the list of the available compressors and their states. +.TP +.B -t, --test-compression +Call decompress after every compress - and compare the result with the original data -, and +some other check. +.TP +.B -h, --help +Display help text. +.TP +.B -v, --verbose +Verbose operation. +.TP +.B -V, --version +Display version information. +.TP +.B -i, --incremental=FILE +Generate an appendage image for FILE. If FILE is written to flash and flash +is appended with the output, then it seems as if it was one thing. + +.SH LIMITATIONS +The format and grammar of the device table file does not allow it to +create symbolic links when the symbolic links are not already present +in the root working directory. + +However, symbolic links may be specified in the device table file +using the \fIl\fR type for the purposes of setting their permissions +and ownership. +.SH BUGS +JFFS2 limits device major and minor numbers to 8 bits each.  Some +consider this a bug. + +.B mkfs.jffs2 +does not properly handle hard links in the input directory structure. +Currently, hard linked files will be expanded to multiple identical +files in the output image. +.SH AUTHORS +David Woodhouse +.br +Manual page written by David Schleef <ds@schleef.org> +.SH SEE ALSO +.BR mkfs (8), +.BR mkfs.jffs (1), +.BR fakeroot (1) diff --git a/jffsX-utils/mkfs.jffs2.c b/jffsX-utils/mkfs.jffs2.c new file mode 100644 index 0000000..f09c0b2 --- /dev/null +++ b/jffsX-utils/mkfs.jffs2.c @@ -0,0 +1,1805 @@ +/* vi: set sw=4 ts=4: */ +/* + * Build a JFFS2 image in a file, from a given directory tree. + * + * Copyright 2001, 2002 Red Hat, Inc. + *           2001 David A. Schleef <ds@lineo.com> + *           2002 Axis Communications AB + *           2001, 2002 Erik Andersen <andersen@codepoet.org> + *           2004 University of Szeged, Hungary + *           2006 KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * Cross-endian support added by David Schleef <ds@schleef.org>. + * + * Major architectural rewrite by Erik Andersen <andersen@codepoet.org> + * to allow support for making hard links (though hard links support is + * not yet implemented), and for munging file permissions and ownership + * on the fly using --faketime, --squash, --devtable.   And I plugged a + * few memory leaks, adjusted the error handling and fixed some little + * nits here and there. + * + * I also added a sample device table file.  See device_table.txt + *  -Erik, September 2001 + * + * Cleanmarkers support added by Axis Communications AB + * + * Rewritten again.  Cleanly separated host and target filsystem + * activities (mainly so I can reuse all the host handling stuff as I + * rewrite other mkfs utils).  Added a verbose option to list types + * and attributes as files are added to the file system.  Major cleanup + * and scrubbing of the code so it can be read, understood, and + * modified by mere mortals. + * + *  -Erik, November 2002 + */ + +#define PROGRAM_NAME "mkfs.jffs2" + +#include <sys/types.h> +#include <stdio.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <dirent.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <stdint.h> +#include <libgen.h> +#include <ctype.h> +#include <time.h> +#include <getopt.h> +#ifndef WITHOUT_XATTR +#include <sys/xattr.h> +#include <sys/acl.h> +#endif +#include <byteswap.h> +#include <crc32.h> +#include <inttypes.h> + +#include "rbtree.h" +#include "common.h" + +/* Do not use the weird XPG version of basename */ +#undef basename + +//#define DMALLOC +//#define mkfs_debug_msg    errmsg +#define mkfs_debug_msg(a...)	{ } + +#define PAD(x) (((x)+3)&~3) + +struct filesystem_entry { +	char *name;					/* Name of this directory (think basename) */ +	char *path;					/* Path of this directory (think dirname) */ +	char *fullname;				/* Full name of this directory (i.e. path+name) */ +	char *hostname;				/* Full path to this file on the host filesystem */ +	uint32_t ino;				/* Inode number of this file in JFFS2 */ +	struct stat sb;				/* Stores directory permissions and whatnot */ +	char *link;					/* Target a symlink points to. */ +	struct filesystem_entry *parent;	/* Parent directory */ +	struct filesystem_entry *prev;	/* Only relevant to non-directories */ +	struct filesystem_entry *next;	/* Only relevant to non-directories */ +	struct filesystem_entry *files;	/* Only relevant to directories */ +	struct rb_node hardlink_rb; +}; + +struct rb_root hardlinks; +static int out_fd = -1; +static int in_fd = -1; +static char default_rootdir[] = "."; +static char *rootdir = default_rootdir; +static int verbose = 0; +static int squash_uids = 0; +static int squash_perms = 0; +static int fake_times = 0; +int target_endian = __BYTE_ORDER; + +uint32_t find_hardlink(struct filesystem_entry *e) +{ +	struct filesystem_entry *f; +	struct rb_node **n = &hardlinks.rb_node; +	struct rb_node *parent = NULL; + +	while (*n) { +		parent = *n; +		f = rb_entry(parent, struct filesystem_entry, hardlink_rb); + +		if ((f->sb.st_dev < e->sb.st_dev) || +		    (f->sb.st_dev == e->sb.st_dev && +		     f->sb.st_ino < e->sb.st_ino)) +			n = &parent->rb_left; +		else if ((f->sb.st_dev > e->sb.st_dev) || +			 (f->sb.st_dev == e->sb.st_dev && +			  f->sb.st_ino > e->sb.st_ino)) { +			n = &parent->rb_right; +		} else +			return f->ino; +	} + +	rb_link_node(&e->hardlink_rb, parent, n); +	rb_insert_color(&e->hardlink_rb, &hardlinks); +	return 0; +} + +extern char *xreadlink(const char *path) +{ +	static const int GROWBY = 80; /* how large we will grow strings by */ + +	char *buf = NULL; +	int bufsize = 0, readsize = 0; + +	do { +		buf = xrealloc(buf, bufsize += GROWBY); +		readsize = readlink(path, buf, bufsize); /* 1st try */ +		if (readsize == -1) { +			sys_errmsg("%s:%s", PROGRAM_NAME, path); +			return NULL; +		} +	} +	while (bufsize < readsize + 1); + +	buf[readsize] = '\0'; + +	return buf; +} +static FILE *xfopen(const char *path, const char *mode) +{ +	FILE *fp; +	if ((fp = fopen(path, mode)) == NULL) +		sys_errmsg_die("%s", path); +	return fp; +} + +static struct filesystem_entry *find_filesystem_entry( +		struct filesystem_entry *dir, char *fullname, uint32_t type) +{ +	struct filesystem_entry *e = dir; + +	if (S_ISDIR(dir->sb.st_mode)) { +		/* If this is the first call, and we actually want this +		 * directory, then return it now */ +		if (strcmp(fullname, e->fullname) == 0) +			return e; + +		e = dir->files; +	} +	while (e) { +		if (S_ISDIR(e->sb.st_mode)) { +			int len = strlen(e->fullname); + +			/* Check if we are a parent of the correct path */ +			if (strncmp(e->fullname, fullname, len) == 0) { +				/* Is this an _exact_ match? */ +				if (strcmp(fullname, e->fullname) == 0) { +					return (e); +				} +				/* Looks like we found a parent of the correct path */ +				if (fullname[len] == '/') { +					if (e->files) { +						return (find_filesystem_entry (e, fullname, type)); +					} else { +						return NULL; +					} +				} +			} +		} else { +			if (strcmp(fullname, e->fullname) == 0) { +				return (e); +			} +		} +		e = e->next; +	} +	return (NULL); +} + +static struct filesystem_entry *add_host_filesystem_entry(const char *name, +		const char *path, unsigned long uid, unsigned long gid, +		unsigned long mode, dev_t rdev, struct filesystem_entry *parent) +{ +	int status; +	char *tmp; +	struct stat sb; +	time_t timestamp = time(NULL); +	struct filesystem_entry *entry; + +	memset(&sb, 0, sizeof(struct stat)); +	status = lstat(path, &sb); + +	if (status >= 0) { +		/* It is ok for some types of files to not exit on disk (such as +		 * device nodes), but if they _do_ exist the specified mode had +		 * better match the actual file or strange things will happen.... */ +		if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) { +			errmsg_die ("%s: file type does not match specified type!", path); +		} +		timestamp = sb.st_mtime; +	} else { +		/* If this is a regular file, it _must_ exist on disk */ +		if ((mode & S_IFMT) == S_IFREG) { +			errmsg_die("%s: does not exist!", path); +		} +	} + +	/* Squash all permissions so files are owned by root, all +	 * timestamps are _right now_, and file permissions +	 * have group and other write removed */ +	if (squash_uids) { +		uid = gid = 0; +	} +	if (squash_perms) { +		if (!S_ISLNK(mode)) { +			mode &= ~(S_IWGRP | S_IWOTH); +			mode &= ~(S_ISUID | S_ISGID); +		} +	} +	if (fake_times) { +		timestamp = 0; +	} + +	entry = xcalloc(1, sizeof(struct filesystem_entry)); + +	entry->hostname = xstrdup(path); +	entry->fullname = xstrdup(name); +	tmp = xstrdup(name); +	entry->name = xstrdup(basename(tmp)); +	free(tmp); +	tmp = xstrdup(name); +	entry->path = xstrdup(dirname(tmp)); +	free(tmp); + +	entry->sb.st_ino = sb.st_ino; +	entry->sb.st_dev = sb.st_dev; +	entry->sb.st_nlink = sb.st_nlink; + +	entry->sb.st_uid = uid; +	entry->sb.st_gid = gid; +	entry->sb.st_mode = mode; +	entry->sb.st_rdev = rdev; +	entry->sb.st_atime = entry->sb.st_ctime = +		entry->sb.st_mtime = timestamp; +	if (S_ISREG(mode)) { +		entry->sb.st_size = sb.st_size; +	} +	if (S_ISLNK(mode)) { +		entry->link = xreadlink(path); +		entry->sb.st_size = strlen(entry->link); +	} + +	/* This happens only for root */ +	if (!parent) +		return (entry); + +	/* Hook the file into the parent directory */ +	entry->parent = parent; +	if (!parent->files) { +		parent->files = entry; +	} else { +		struct filesystem_entry *prev; +		for (prev = parent->files; prev->next; prev = prev->next); +		prev->next = entry; +		entry->prev = prev; +	} + +	return (entry); +} + +static struct filesystem_entry *recursive_add_host_directory( +		struct filesystem_entry *parent, const char *targetpath, +		const char *hostpath) +{ +	int i, n; +	struct stat sb; +	char *hpath, *tpath; +	struct dirent *dp, **namelist; +	struct filesystem_entry *entry; + + +	if (lstat(hostpath, &sb)) { +		sys_errmsg_die("%s", hostpath); +	} + +	entry = add_host_filesystem_entry(targetpath, hostpath, +			sb.st_uid, sb.st_gid, sb.st_mode, 0, parent); + +	n = scandir(hostpath, &namelist, 0, alphasort); +	if (n < 0) { +		sys_errmsg_die("opening directory %s", hostpath); +	} + +	for (i=0; i<n; i++) +	{ +		dp = namelist[i]; +		if (dp->d_name[0] == '.' && (dp->d_name[1] == 0 || +					(dp->d_name[1] == '.' &&  dp->d_name[2] == 0))) +		{ +			free(dp); +			continue; +		} + +		xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); +		if (lstat(hpath, &sb)) { +			sys_errmsg_die("%s", hpath); +		} +		if (strcmp(targetpath, "/") == 0) { +			xasprintf(&tpath, "%s%s", targetpath, dp->d_name); +		} else { +			xasprintf(&tpath, "%s/%s", targetpath, dp->d_name); +		} + +		switch (sb.st_mode & S_IFMT) { +			case S_IFDIR: +				recursive_add_host_directory(entry, tpath, hpath); +				break; + +			case S_IFREG: +			case S_IFSOCK: +			case S_IFIFO: +			case S_IFLNK: +			case S_IFCHR: +			case S_IFBLK: +				add_host_filesystem_entry(tpath, hpath, sb.st_uid, +						sb.st_gid, sb.st_mode, sb.st_rdev, entry); +				break; + +			default: +				errmsg("Unknown file type %o for %s", sb.st_mode, hpath); +				break; +		} +		free(dp); +		free(hpath); +		free(tpath); +	} +	free(namelist); +	return (entry); +} + +/* the GNU C library has a wonderful scanf("%as", string) which will +   allocate the string with the right size, good to avoid buffer overruns. +   the following macros use it if available or use a hacky workaround... + */ + +#ifdef __GNUC__ +#define SCANF_PREFIX "a" +#define SCANF_STRING(s) (&s) +#define GETCWD_SIZE 0 +#else +#define SCANF_PREFIX "511" +#define SCANF_STRING(s) (s = xmalloc(512)) +#define GETCWD_SIZE -1 +inline int snprintf(char *str, size_t n, const char *fmt, ...) +{ +	int ret; +	va_list ap; + +	va_start(ap, fmt); +	ret = vsprintf(str, fmt, ap); +	va_end(ap); +	return ret; +} +#endif + +/*  device table entries take the form of: +	<path>	<type> <mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count> +	/dev/mem     c    640       0       0         1       1       0     0         - + +	type can be one of: +	f	A regular file +	d	Directory +	c	Character special device file +	b	Block special device file +	p	Fifo (named pipe) + +	I don't bother with symlinks (permissions are irrelevant), hard +	links (special cases of regular files), or sockets (why bother). + +	Regular files must exist in the target root directory.  If a char, +	block, fifo, or directory does not exist, it will be created. + */ +static int interpret_table_entry(struct filesystem_entry *root, char *line) +{ +	char *hostpath; +	char type, *name = NULL, *tmp, *dir; +	unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; +	unsigned long start = 0, increment = 1, count = 0; +	struct filesystem_entry *parent, *entry; + +	if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu", +				SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor, +				&start, &increment, &count) < 0) +	{ +		return 1; +	} + +	if (!strcmp(name, "/")) { +		errmsg_die("Device table entries require absolute paths"); +	} + +	xasprintf(&hostpath, "%s%s", rootdir, name); + +	/* Check if this file already exists... */ +	switch (type) { +		case 'd': +			mode |= S_IFDIR; +			break; +		case 'f': +			mode |= S_IFREG; +			break; +		case 'p': +			mode |= S_IFIFO; +			break; +		case 'c': +			mode |= S_IFCHR; +			break; +		case 'b': +			mode |= S_IFBLK; +			break; +		case 'l': +			mode |= S_IFLNK; +			break; +		default: +			errmsg_die("Unsupported file type '%c'", type); +	} +	entry = find_filesystem_entry(root, name, mode); +	if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { +		/* Ok, we just need to fixup the existing entry +		 * and we will be all done... */ +		entry->sb.st_uid = uid; +		entry->sb.st_gid = gid; +		entry->sb.st_mode = mode; +		if (major && minor) { +			entry->sb.st_rdev = makedev(major, minor); +		} +	} else { +		/* If parent is NULL (happens with device table entries), +		 * try and find our parent now) */ +		tmp = xstrdup(name); +		dir = dirname(tmp); +		parent = find_filesystem_entry(root, dir, S_IFDIR); +		free(tmp); +		if (parent == NULL) { +			errmsg ("skipping device_table entry '%s': no parent directory!", name); +			free(name); +			free(hostpath); +			return 1; +		} + +		switch (type) { +			case 'd': +				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); +				break; +			case 'f': +				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); +				break; +			case 'p': +				add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); +				break; +			case 'c': +			case 'b': +				if (count > 0) { +					dev_t rdev; +					unsigned long i; +					char *dname, *hpath; + +					for (i = start; i < (start + count); i++) { +						xasprintf(&dname, "%s%lu", name, i); +						xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); +						rdev = makedev(major, minor + (i - start) * increment); +						add_host_filesystem_entry(dname, hpath, uid, gid, +								mode, rdev, parent); +						free(dname); +						free(hpath); +					} +				} else { +					dev_t rdev = makedev(major, minor); +					add_host_filesystem_entry(name, hostpath, uid, gid, +							mode, rdev, parent); +				} +				break; +			default: +				errmsg_die("Unsupported file type '%c'", type); +		} +	} +	free(name); +	free(hostpath); +	return 0; +} + +static int parse_device_table(struct filesystem_entry *root, FILE * file) +{ +	char *line; +	int status = 0; +	size_t length = 0; + +	/* Turn off squash, since we must ensure that values +	 * entered via the device table are not squashed */ +	squash_uids = 0; +	squash_perms = 0; + +	/* Looks ok so far.  The general plan now is to read in one +	 * line at a time, check for leading comment delimiters ('#'), +	 * then try and parse the line as a device table.  If we fail +	 * to parse things, try and help the poor fool to fix their +	 * device table with a useful error msg... */ +	line = NULL; +	while (getline(&line, &length, file) != -1) { +		/* First trim off any whitespace */ +		int len = strlen(line); + +		/* trim trailing whitespace */ +		while (len > 0 && isspace(line[len - 1])) +			line[--len] = '\0'; +		/* trim leading whitespace */ +		memmove(line, &line[strspn(line, " \n\r\t\v")], len); + +		/* How long are we after trimming? */ +		len = strlen(line); + +		/* If this is NOT a comment line, try to interpret it */ +		if (len && *line != '#') { +			if (interpret_table_entry(root, line)) +				status = 1; +		} + +		free(line); +		line = NULL; +	} +	fclose(file); + +	return status; +} + +static void cleanup(struct filesystem_entry *dir) +{ +	struct filesystem_entry *e, *prev; + +	e = dir->files; +	while (e) { +		if (e->name) +			free(e->name); +		if (e->path) +			free(e->path); +		if (e->fullname) +			free(e->fullname); +		e->next = NULL; +		e->name = NULL; +		e->path = NULL; +		e->fullname = NULL; +		e->prev = NULL; +		prev = e; +		if (S_ISDIR(e->sb.st_mode)) { +			cleanup(e); +		} +		e = e->next; +		free(prev); +	} +} + +/* Here is where we do the actual creation of the file system */ +#include "mtd/jffs2-user.h" + +#define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF +#ifndef JFFS2_MAX_SYMLINK_LEN +#define JFFS2_MAX_SYMLINK_LEN 254 +#endif + +static uint32_t ino = 0; +static uint8_t *file_buffer = NULL;		/* file buffer contains the actual erase block*/ +static int out_ofs = 0; +static int erase_block_size = 65536; +static int pad_fs_size = 0; +static int add_cleanmarkers = 1; +static struct jffs2_unknown_node cleanmarker; +static int cleanmarker_size = sizeof(cleanmarker); +static unsigned char ffbuf[16] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +	0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* We set this at start of main() using sysconf(), -1 means we don't know */ +/* When building an fs for non-native systems, use --pagesize=SIZE option */ +int page_size = -1; + +#include "compr.h" + +static void full_write(int fd, const void *buf, int len) +{ +	int ret; + +	while (len > 0) { +		ret = write(fd, buf, len); + +		if (ret < 0) +			sys_errmsg_die("write"); + +		if (ret == 0) +			sys_errmsg_die("write returned zero"); + +		len -= ret; +		buf += ret; +		out_ofs += ret; +	} +} + +static void padblock(void) +{ +	while (out_ofs % erase_block_size) { +		full_write(out_fd, ffbuf, min(sizeof(ffbuf), +					erase_block_size - (out_ofs % erase_block_size))); +	} +} + +static void pad(int req) +{ +	while (req) { +		if (req > sizeof(ffbuf)) { +			full_write(out_fd, ffbuf, sizeof(ffbuf)); +			req -= sizeof(ffbuf); +		} else { +			full_write(out_fd, ffbuf, req); +			req = 0; +		} +	} +} + +static inline void padword(void) +{ +	if (out_ofs % 4) { +		full_write(out_fd, ffbuf, 4 - (out_ofs % 4)); +	} +} + +static inline void pad_block_if_less_than(int req) +{ +	if (add_cleanmarkers) { +		if ((out_ofs % erase_block_size) == 0) { +			full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); +			pad(cleanmarker_size - sizeof(cleanmarker)); +			padword(); +		} +	} +	if ((out_ofs % erase_block_size) + req > erase_block_size) { +		padblock(); +	} +	if (add_cleanmarkers) { +		if ((out_ofs % erase_block_size) == 0) { +			full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); +			pad(cleanmarker_size - sizeof(cleanmarker)); +			padword(); +		} +	} +} + +static void write_dirent(struct filesystem_entry *e) +{ +	char *name = e->name; +	struct jffs2_raw_dirent rd; +	struct stat *statbuf = &(e->sb); +	static uint32_t version = 0; + +	memset(&rd, 0, sizeof(rd)); + +	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); +	rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); +	rd.hdr_crc = cpu_to_je32(mtd_crc32(0, &rd, +				sizeof(struct jffs2_unknown_node) - 4)); +	rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); +	rd.version = cpu_to_je32(version++); +	rd.ino = cpu_to_je32(e->ino); +	rd.mctime = cpu_to_je32(statbuf->st_mtime); +	rd.nsize = strlen(name); +	rd.type = IFTODT(statbuf->st_mode); +	//rd.unused[0] = 0; +	//rd.unused[1] = 0; +	rd.node_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(rd) - 8)); +	rd.name_crc = cpu_to_je32(mtd_crc32(0, name, strlen(name))); + +	pad_block_if_less_than(sizeof(rd) + rd.nsize); +	full_write(out_fd, &rd, sizeof(rd)); +	full_write(out_fd, name, rd.nsize); +	padword(); +} + +static unsigned int write_regular_file(struct filesystem_entry *e) +{ +	int fd, len; +	uint32_t ver; +	unsigned int offset; +	unsigned char *buf, *cbuf, *wbuf; +	struct jffs2_raw_inode ri; +	struct stat *statbuf; +	unsigned int totcomp = 0; + +	statbuf = &(e->sb); +	if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { +		errmsg("Skipping file \"%s\" too large.", e->path); +		return -1; +	} +	fd = open(e->hostname, O_RDONLY); +	if (fd == -1) { +		sys_errmsg_die("%s: open file", e->hostname); +	} + +	e->ino = ++ino; +	mkfs_debug_msg("writing file '%s'  ino=%lu  parent_ino=%lu", +			e->name, (unsigned long) e->ino, +			(unsigned long) e->parent->ino); +	write_dirent(e); + +	buf = xmalloc(page_size); +	cbuf = NULL; + +	ver = 0; +	offset = 0; + +	memset(&ri, 0, sizeof(ri)); +	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + +	ri.ino = cpu_to_je32(e->ino); +	ri.mode = cpu_to_jemode(statbuf->st_mode); +	ri.uid = cpu_to_je16(statbuf->st_uid); +	ri.gid = cpu_to_je16(statbuf->st_gid); +	ri.atime = cpu_to_je32(statbuf->st_atime); +	ri.ctime = cpu_to_je32(statbuf->st_ctime); +	ri.mtime = cpu_to_je32(statbuf->st_mtime); +	ri.isize = cpu_to_je32(statbuf->st_size); + +	while ((len = read(fd, buf, page_size))) { +		unsigned char *tbuf = buf; + +		if (len < 0) { +			sys_errmsg_die("read"); +		} + +		while (len) { +			uint32_t dsize, space; +			uint16_t compression; + +			pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN); + +			dsize = len; +			space = +				erase_block_size - (out_ofs % erase_block_size) - +				sizeof(ri); +			if (space > dsize) +				space = dsize; + +			compression = jffs2_compress(tbuf, &cbuf, &dsize, &space); + +			ri.compr = compression & 0xff; +			ri.usercompr = (compression >> 8) & 0xff; + +			if (ri.compr) { +				wbuf = cbuf; +			} else { +				wbuf = tbuf; +				dsize = space; +			} + +			ri.totlen = cpu_to_je32(sizeof(ri) + space); +			ri.hdr_crc = cpu_to_je32(mtd_crc32(0, +						&ri, sizeof(struct jffs2_unknown_node) - 4)); + +			ri.version = cpu_to_je32(++ver); +			ri.offset = cpu_to_je32(offset); +			ri.csize = cpu_to_je32(space); +			ri.dsize = cpu_to_je32(dsize); +			ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); +			ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); + +			full_write(out_fd, &ri, sizeof(ri)); +			totcomp += sizeof(ri); +			full_write(out_fd, wbuf, space); +			totcomp += space; +			padword(); + +			if (tbuf != cbuf) { +				free(cbuf); +				cbuf = NULL; +			} + +			tbuf += dsize; +			len -= dsize; +			offset += dsize; + +		} +	} +	if (!je32_to_cpu(ri.version)) { +		/* Was empty file */ +		pad_block_if_less_than(sizeof(ri)); + +		ri.version = cpu_to_je32(++ver); +		ri.totlen = cpu_to_je32(sizeof(ri)); +		ri.hdr_crc = cpu_to_je32(mtd_crc32(0, +					&ri, sizeof(struct jffs2_unknown_node) - 4)); +		ri.csize = cpu_to_je32(0); +		ri.dsize = cpu_to_je32(0); +		ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); + +		full_write(out_fd, &ri, sizeof(ri)); +		padword(); +	} +	free(buf); +	close(fd); +	return totcomp; +} + +static void write_symlink(struct filesystem_entry *e) +{ +	int len; +	struct stat *statbuf; +	struct jffs2_raw_inode ri; + +	statbuf = &(e->sb); +	e->ino = ++ino; +	mkfs_debug_msg("writing symlink '%s'  ino=%lu  parent_ino=%lu", +			e->name, (unsigned long) e->ino, +			(unsigned long) e->parent->ino); +	write_dirent(e); + +	len = strlen(e->link); +	if (len > JFFS2_MAX_SYMLINK_LEN) { +		errmsg("symlink too large. Truncated to %d chars.", +				JFFS2_MAX_SYMLINK_LEN); +		len = JFFS2_MAX_SYMLINK_LEN; +	} + +	memset(&ri, 0, sizeof(ri)); + +	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); +	ri.totlen = cpu_to_je32(sizeof(ri) + len); +	ri.hdr_crc = cpu_to_je32(mtd_crc32(0, +				&ri, sizeof(struct jffs2_unknown_node) - 4)); + +	ri.ino = cpu_to_je32(e->ino); +	ri.mode = cpu_to_jemode(statbuf->st_mode); +	ri.uid = cpu_to_je16(statbuf->st_uid); +	ri.gid = cpu_to_je16(statbuf->st_gid); +	ri.atime = cpu_to_je32(statbuf->st_atime); +	ri.ctime = cpu_to_je32(statbuf->st_ctime); +	ri.mtime = cpu_to_je32(statbuf->st_mtime); +	ri.isize = cpu_to_je32(statbuf->st_size); +	ri.version = cpu_to_je32(1); +	ri.csize = cpu_to_je32(len); +	ri.dsize = cpu_to_je32(len); +	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); +	ri.data_crc = cpu_to_je32(mtd_crc32(0, e->link, len)); + +	pad_block_if_less_than(sizeof(ri) + len); +	full_write(out_fd, &ri, sizeof(ri)); +	full_write(out_fd, e->link, len); +	padword(); +} + +static void write_pipe(struct filesystem_entry *e) +{ +	struct stat *statbuf; +	struct jffs2_raw_inode ri; + +	statbuf = &(e->sb); +	e->ino = ++ino; +	if (S_ISDIR(statbuf->st_mode)) { +		mkfs_debug_msg("writing dir '%s'  ino=%lu  parent_ino=%lu", +				e->name, (unsigned long) e->ino, +				(unsigned long) (e->parent) ? e->parent->ino : 1); +	} +	write_dirent(e); + +	memset(&ri, 0, sizeof(ri)); + +	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); +	ri.totlen = cpu_to_je32(sizeof(ri)); +	ri.hdr_crc = cpu_to_je32(mtd_crc32(0, +				&ri, sizeof(struct jffs2_unknown_node) - 4)); + +	ri.ino = cpu_to_je32(e->ino); +	ri.mode = cpu_to_jemode(statbuf->st_mode); +	ri.uid = cpu_to_je16(statbuf->st_uid); +	ri.gid = cpu_to_je16(statbuf->st_gid); +	ri.atime = cpu_to_je32(statbuf->st_atime); +	ri.ctime = cpu_to_je32(statbuf->st_ctime); +	ri.mtime = cpu_to_je32(statbuf->st_mtime); +	ri.isize = cpu_to_je32(0); +	ri.version = cpu_to_je32(1); +	ri.csize = cpu_to_je32(0); +	ri.dsize = cpu_to_je32(0); +	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); +	ri.data_crc = cpu_to_je32(0); + +	pad_block_if_less_than(sizeof(ri)); +	full_write(out_fd, &ri, sizeof(ri)); +	padword(); +} + +static void write_special_file(struct filesystem_entry *e) +{ +	jint16_t kdev; +	struct stat *statbuf; +	struct jffs2_raw_inode ri; + +	statbuf = &(e->sb); +	e->ino = ++ino; +	write_dirent(e); + +	kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + +			minor(statbuf->st_rdev)); + +	memset(&ri, 0, sizeof(ri)); + +	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); +	ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); +	ri.hdr_crc = cpu_to_je32(mtd_crc32(0, +				&ri, sizeof(struct jffs2_unknown_node) - 4)); + +	ri.ino = cpu_to_je32(e->ino); +	ri.mode = cpu_to_jemode(statbuf->st_mode); +	ri.uid = cpu_to_je16(statbuf->st_uid); +	ri.gid = cpu_to_je16(statbuf->st_gid); +	ri.atime = cpu_to_je32(statbuf->st_atime); +	ri.ctime = cpu_to_je32(statbuf->st_ctime); +	ri.mtime = cpu_to_je32(statbuf->st_mtime); +	ri.isize = cpu_to_je32(statbuf->st_size); +	ri.version = cpu_to_je32(1); +	ri.csize = cpu_to_je32(sizeof(kdev)); +	ri.dsize = cpu_to_je32(sizeof(kdev)); +	ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); +	ri.data_crc = cpu_to_je32(mtd_crc32(0, &kdev, sizeof(kdev))); + +	pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); +	full_write(out_fd, &ri, sizeof(ri)); +	full_write(out_fd, &kdev, sizeof(kdev)); +	padword(); +} + +#ifndef WITHOUT_XATTR +typedef struct xattr_entry { +	struct xattr_entry *next; +	uint32_t xid; +	int xprefix; +	char *xname; +	char *xvalue; +	int name_len; +	int value_len; +} xattr_entry_t; + +#define XATTR_BUFFER_SIZE		(64 * 1024)	/* 64KB */ +static uint32_t enable_xattr = 0; +static uint32_t highest_xid = 0; +static uint32_t highest_xseqno = 0; + +static struct { +	int xprefix; +	const char *string; +	int length; +} xprefix_tbl[] = { +	{ JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, +	{ JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, +	{ JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, +	{ JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, +	{ JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, +	{ 0, NULL, 0 } +}; + +static void formalize_posix_acl(void *xvalue, int *value_len) +{ +	struct posix_acl_xattr_header *pacl_header; +	struct posix_acl_xattr_entry *pent, *plim; +	struct jffs2_acl_header *jacl_header; +	struct jffs2_acl_entry *jent; +	struct jffs2_acl_entry_short *jent_s; +	char buffer[XATTR_BUFFER_SIZE]; +	int offset = 0; + +	pacl_header = xvalue;; +	pent = pacl_header->a_entries; +	plim = xvalue + *value_len; + +	jacl_header = (struct jffs2_acl_header *)buffer; +	offset += sizeof(struct jffs2_acl_header); +	jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + +	while (pent < plim) { +		switch(le16_to_cpu(pent->e_tag)) { +			case ACL_USER_OBJ: +			case ACL_GROUP_OBJ: +			case ACL_MASK: +			case ACL_OTHER: +				jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); +				offset += sizeof(struct jffs2_acl_entry_short); +				jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); +				jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); +				break; +			case ACL_USER: +			case ACL_GROUP: +				jent = (struct jffs2_acl_entry *)(buffer + offset); +				offset += sizeof(struct jffs2_acl_entry); +				jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); +				jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); +				jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); +				break; +			default: +				printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); +				exit(1); +		} +		pent++; +	} +	if (offset > *value_len) { +		printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", +				offset, *value_len); +		exit(1); +	} +	memcpy(xvalue, buffer, offset); +	*value_len = offset; +} + +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ +	xattr_entry_t *xe; +	struct jffs2_raw_xattr rx; +	int name_len; + +	/* create xattr entry */ +	name_len = strlen(xname); +	xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); +	xe->next = NULL; +	xe->xid = ++highest_xid; +	xe->xprefix = xprefix; +	xe->xname = ((char *)xe) + sizeof(xattr_entry_t); +	xe->xvalue = xe->xname + name_len + 1; +	xe->name_len = name_len; +	xe->value_len = value_len; +	strcpy(xe->xname, xname); +	memcpy(xe->xvalue, xvalue, value_len); + +	/* write xattr node */ +	memset(&rx, 0, sizeof(rx)); +	rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); +	rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); +	rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + +	rx.xid = cpu_to_je32(xe->xid); +	rx.version = cpu_to_je32(1);	/* initial version */ +	rx.xprefix = xprefix; +	rx.name_len = xe->name_len; +	rx.value_len = cpu_to_je16(xe->value_len); +	rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); +	rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); + +	pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); +	full_write(out_fd, &rx, sizeof(rx)); +	full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); +	padword(); + +	return xe; +} + +#define XATTRENTRY_HASHSIZE	57 +static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ +	static xattr_entry_t **xentry_hash = NULL; +	xattr_entry_t *xe; +	int index, name_len; + +	/* create hash table */ +	if (!xentry_hash) +		xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); + +	if (xprefix == JFFS2_XPREFIX_ACL_ACCESS +			|| xprefix == JFFS2_XPREFIX_ACL_DEFAULT) +		formalize_posix_acl(xvalue, &value_len); + +	name_len = strlen(xname); +	index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; +	for (xe = xentry_hash[index]; xe; xe = xe->next) { +		if (xe->xprefix == xprefix +				&& xe->value_len == value_len +				&& !strcmp(xe->xname, xname) +				&& !memcmp(xe->xvalue, xvalue, value_len)) +			break; +	} +	if (!xe) { +		xe = create_xattr_entry(xprefix, xname, xvalue, value_len); +		xe->next = xentry_hash[index]; +		xentry_hash[index] = xe; +	} +	return xe; +} + +static void write_xattr_entry(struct filesystem_entry *e) +{ +	struct jffs2_raw_xref ref; +	struct xattr_entry *xe; +	char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; +	char *xname; +	const char *prefix_str; +	int i, xprefix, prefix_len; +	int list_sz, offset, name_len, value_len; + +	if (!enable_xattr) +		return; + +	list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); +	if (list_sz < 0) { +		if (verbose) +			printf("llistxattr('%s') = %d : %s\n", +					e->hostname, errno, strerror(errno)); +		return; +	} + +	for (offset = 0; offset < list_sz; offset += name_len) { +		xname = xlist + offset; +		name_len = strlen(xname) + 1; + +		for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { +			prefix_str = xprefix_tbl[i].string; +			prefix_len = xprefix_tbl[i].length; +			if (prefix_str[prefix_len - 1] == '.') { +				if (!strncmp(xname, prefix_str, prefix_len - 1)) +					break; +			} else { +				if (!strcmp(xname, prefix_str)) +					break; +			} +		} +		if (!xprefix) { +			if (verbose) +				printf("%s: xattr '%s' is not supported.\n", +						e->hostname, xname); +			continue; +		} +		if ((enable_xattr & (1 << xprefix)) == 0) +			continue; + +		value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); +		if (value_len < 0) { +			if (verbose) +				printf("lgetxattr('%s', '%s') = %d : %s\n", +						e->hostname, xname, errno, strerror(errno)); +			continue; +		} +		xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); +		if (!xe) { +			if (verbose) +				printf("%s : xattr '%s' was ignored.\n", +						e->hostname, xname); +			continue; +		} + +		memset(&ref, 0, sizeof(ref)); +		ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +		ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); +		ref.totlen = cpu_to_je32(sizeof(ref)); +		ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); +		ref.ino = cpu_to_je32(e->ino); +		ref.xid = cpu_to_je32(xe->xid); +		ref.xseqno = cpu_to_je32(highest_xseqno += 2); +		ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); + +		pad_block_if_less_than(sizeof(ref)); +		full_write(out_fd, &ref, sizeof(ref)); +		padword(); +	} +} + +#else /* WITHOUT_XATTR */ +#define write_xattr_entry(x) +#endif + +static void recursive_populate_directory(struct filesystem_entry *dir) +{ +	struct filesystem_entry *e; +	unsigned int wrote; + +	if (verbose) { +		printf("%s\n", dir->fullname); +	} +	write_xattr_entry(dir);		/* for '/' */ + +	e = dir->files; +	while (e) { +		if (e->sb.st_nlink >= 1 && +		    (e->ino = find_hardlink(e))) { + +			write_dirent(e); +			if (verbose) { +				printf("\tL %04o %9lu             %5d:%-3d %s\n", +				       e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, +				       (int) (e->sb.st_uid), (int) (e->sb.st_gid), +				       e->name); +			} +		} else switch (e->sb.st_mode & S_IFMT) { +			case S_IFDIR: +				if (verbose) { +					printf("\td %04o %9" PRIdoff_t "             %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, e->sb.st_size, +							(int) (e->sb.st_uid), (int) (e->sb.st_gid), +							e->name); +				} +				write_pipe(e); +				write_xattr_entry(e); +				break; +			case S_IFSOCK: +				if (verbose) { +					printf("\ts %04o %9" PRIdoff_t "             %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, e->sb.st_size, +							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name); +				} +				write_pipe(e); +				write_xattr_entry(e); +				break; +			case S_IFIFO: +				if (verbose) { +					printf("\tp %04o %9" PRIdoff_t "             %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, e->sb.st_size, +							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name); +				} +				write_pipe(e); +				write_xattr_entry(e); +				break; +			case S_IFCHR: +				if (verbose) { +					printf("\tc %04o %4d,%4d             %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), +							minor(e->sb.st_rdev), (int) e->sb.st_uid, +							(int) e->sb.st_gid, e->name); +				} +				write_special_file(e); +				write_xattr_entry(e); +				break; +			case S_IFBLK: +				if (verbose) { +					printf("\tb %04o %4d,%4d             %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), +							minor(e->sb.st_rdev), (int) e->sb.st_uid, +							(int) e->sb.st_gid, e->name); +				} +				write_special_file(e); +				write_xattr_entry(e); +				break; +			case S_IFLNK: +				if (verbose) { +					printf("\tl %04o %9" PRIdoff_t "             %5d:%-3d %s -> %s\n", +							e->sb.st_mode & ~S_IFMT, e->sb.st_size, +							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name, +							e->link); +				} +				write_symlink(e); +				write_xattr_entry(e); +				break; +			case S_IFREG: +				wrote = write_regular_file(e); +				write_xattr_entry(e); +				if (verbose) { +					printf("\tf %04o %9" PRIdoff_t " (%9u) %5d:%-3d %s\n", +							e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, +							(int) e->sb.st_uid, (int) e->sb.st_gid, e->name); +				} +				break; +			default: +				errmsg("Unknown mode %o for %s", e->sb.st_mode, +						e->fullname); +				break; +		} +		e = e->next; +	} + +	e = dir->files; +	while (e) { +		if (S_ISDIR(e->sb.st_mode)) { +			if (e->files) { +				recursive_populate_directory(e); +			} else if (verbose) { +				printf("%s\n", e->fullname); +			} +		} +		e = e->next; +	} +} + +static void create_target_filesystem(struct filesystem_entry *root) +{ +	cleanmarker.magic    = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); +	cleanmarker.totlen   = cpu_to_je32(cleanmarker_size); +	cleanmarker.hdr_crc  = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); + +	if (ino == 0) +		ino = 1; + +	root->ino = 1; +	recursive_populate_directory(root); + +	if (pad_fs_size == -1) { +		padblock(); +	} else { +		if (pad_fs_size && add_cleanmarkers){ +			padblock(); +			while (out_ofs < pad_fs_size) { +				full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); +				pad(cleanmarker_size - sizeof(cleanmarker)); +				padblock(); +			} +		} else { +			while (out_ofs < pad_fs_size) { +				full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs)); +			} + +		} +	} +} + +static struct option long_options[] = { +	{"pad", 2, NULL, 'p'}, +	{"root", 1, NULL, 'r'}, +	{"pagesize", 1, NULL, 's'}, +	{"eraseblock", 1, NULL, 'e'}, +	{"output", 1, NULL, 'o'}, +	{"help", 0, NULL, 'h'}, +	{"verbose", 0, NULL, 'v'}, +	{"version", 0, NULL, 'V'}, +	{"big-endian", 0, NULL, 'b'}, +	{"little-endian", 0, NULL, 'l'}, +	{"no-cleanmarkers", 0, NULL, 'n'}, +	{"cleanmarker", 1, NULL, 'c'}, +	{"squash", 0, NULL, 'q'}, +	{"squash-uids", 0, NULL, 'U'}, +	{"squash-perms", 0, NULL, 'P'}, +	{"faketime", 0, NULL, 'f'}, +	{"devtable", 1, NULL, 'D'}, +	{"compression-mode", 1, NULL, 'm'}, +	{"disable-compressor", 1, NULL, 'x'}, +	{"enable-compressor", 1, NULL, 'X'}, +	{"test-compression", 0, NULL, 't'}, +	{"compressor-priority", 1, NULL, 'y'}, +	{"incremental", 1, NULL, 'i'}, +#ifndef WITHOUT_XATTR +	{"with-xattr", 0, NULL, 1000 }, +	{"with-selinux", 0, NULL, 1001 }, +	{"with-posix-acl", 0, NULL, 1002 }, +#endif +	{NULL, 0, NULL, 0} +}; + +static const char helptext[] = +"Usage: mkfs.jffs2 [OPTIONS]\n" +"Make a JFFS2 file system image from an existing directory tree\n\n" +"Options:\n" +"  -p, --pad[=SIZE]        Pad output to SIZE bytes with 0xFF. If SIZE is\n" +"                          not specified, the output is padded to the end of\n" +"                          the final erase block\n" +"  -r, -d, --root=DIR      Build file system from directory DIR (default: cwd)\n" +"  -s, --pagesize=SIZE     Use page size (max data node size) SIZE.\n" +"                          Set according to target system's memory management\n" +"                          page size (default: 4KiB)\n" +"  -e, --eraseblock=SIZE   Use erase block size SIZE (default: 64KiB)\n" +"  -c, --cleanmarker=SIZE  Size of cleanmarker (default 12)\n" +"  -m, --compr-mode=MODE   Select compression mode (default: priority)\n" +"  -x, --disable-compressor=COMPRESSOR_NAME\n" +"                          Disable a compressor\n" +"  -X, --enable-compressor=COMPRESSOR_NAME\n" +"                          Enable a compressor\n" +"  -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n" +"                          Set the priority of a compressor\n" +"  -L, --list-compressors  Show the list of the available compressors\n" +"  -t, --test-compression  Call decompress and compare with the original (for test)\n" +"  -n, --no-cleanmarkers   Don't add a cleanmarker to every eraseblock\n" +"  -o, --output=FILE       Output to FILE (default: stdout)\n" +"  -l, --little-endian     Create a little-endian filesystem\n" +"  -b, --big-endian        Create a big-endian filesystem\n" +"  -D, --devtable=FILE     Use the named FILE as a device table file\n" +"  -f, --faketime          Change all file times to '0' for regression testing\n" +"  -q, --squash            Squash permissions and owners making all files be owned by root\n" +"  -U, --squash-uids       Squash owners making all files be owned by root\n" +"  -P, --squash-perms      Squash permissions on all files\n" +#ifndef WITHOUT_XATTR +"      --with-xattr        stuff all xattr entries into image\n" +"      --with-selinux      stuff only SELinux Labels into jffs2 image\n" +"      --with-posix-acl    stuff only POSIX ACL entries into jffs2 image\n" +#endif +"  -h, --help              Display this help text\n" +"  -v, --verbose           Verbose operation\n" +"  -V, --version           Display version information\n" +"  -i, --incremental=FILE  Parse FILE and generate appendage output for it\n\n"; + +static const char revtext[] = "1.60"; + +int load_next_block() { + +	int ret; +	ret = read(in_fd, file_buffer, erase_block_size); + +	if(verbose) +		printf("Load next block : %d bytes read\n",ret); + +	return ret; +} + +void process_buffer(int inp_size) { +	uint8_t		*p = file_buffer; +	union jffs2_node_union 	*node; +	uint16_t	type; +	int		bitchbitmask = 0; +	int		obsolete; + +	char	name[256]; + +	while ( p < (file_buffer + inp_size)) { + +		node = (union jffs2_node_union *) p; + +		/* Skip empty space */ +		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { +			p += 4; +			continue; +		} + +		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{ +			if (!bitchbitmask++) +				printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); +			p += 4; +			continue; +		} + +		bitchbitmask = 0; + +		type = je16_to_cpu(node->u.nodetype); +		if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { +			obsolete = 1; +			type |= JFFS2_NODE_ACCURATE; +		} else +			obsolete = 0; + +		node->u.nodetype = cpu_to_je16(type); + +		switch(je16_to_cpu(node->u.nodetype)) { + +			case JFFS2_NODETYPE_INODE: +				if(verbose) +					printf ("%8s Inode      node at 0x%08zx, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", +							obsolete ? "Obsolete" : "", +							p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), +							je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), +							je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); + +				if ( je32_to_cpu (node->i.ino) > ino ) +					ino = je32_to_cpu (node->i.ino); + +				p += PAD(je32_to_cpu (node->i.totlen)); +				break; + +			case JFFS2_NODETYPE_DIRENT: +				memcpy (name, node->d.name, node->d.nsize); +				name [node->d.nsize] = 0x0; + +				if(verbose) +					printf ("%8s Dirent     node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n", +							obsolete ? "Obsolete" : "", +							p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), +							je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), +							node->d.nsize, name); + +				p += PAD(je32_to_cpu (node->d.totlen)); +				break; + +			case JFFS2_NODETYPE_CLEANMARKER: +				if (verbose) { +					printf ("%8s Cleanmarker     at 0x%08zx, totlen 0x%08x\n", +							obsolete ? "Obsolete" : "", +							p - file_buffer, je32_to_cpu (node->u.totlen)); +				} + +				p += PAD(je32_to_cpu (node->u.totlen)); +				break; + +			case JFFS2_NODETYPE_PADDING: +				if (verbose) { +					printf ("%8s Padding    node at 0x%08zx, totlen 0x%08x\n", +							obsolete ? "Obsolete" : "", +							p - file_buffer, je32_to_cpu (node->u.totlen)); +				} + +				p += PAD(je32_to_cpu (node->u.totlen)); +				break; + +			case 0xffff: +				p += 4; +				break; + +			default: +				if (verbose) { +					printf ("%8s Unknown    node at 0x%08zx, totlen 0x%08x\n", +							obsolete ? "Obsolete" : "", +							p - file_buffer, je32_to_cpu (node->u.totlen)); +				} + +				p += PAD(je32_to_cpu (node->u.totlen)); +		} +	} +} + +void parse_image(){ +	int ret; + +	file_buffer = xmalloc(erase_block_size); + +	while ((ret = load_next_block())) { +		process_buffer(ret); +	} + +	if (file_buffer) +		free(file_buffer); + +	close(in_fd); +} + +int main(int argc, char **argv) +{ +	int c, opt; +	char *cwd; +	struct stat sb; +	FILE *devtable = NULL; +	struct filesystem_entry *root; +	char *compr_name = NULL; +	int compr_prior  = -1; +	int warn_page_size = 0; + +	page_size = sysconf(_SC_PAGESIZE); +	if (page_size < 0) /* System doesn't know so ... */ +		page_size = 4096; /* ... we make an educated guess */ +	if (page_size != 4096) +		warn_page_size = 1; /* warn user if page size not 4096 */ + +	jffs2_compressors_init(); + +	while ((opt = getopt_long(argc, argv, +					"D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0) +	{ +		switch (opt) { +			case 'D': +				devtable = xfopen(optarg, "r"); +				if (fstat(fileno(devtable), &sb) < 0) +					sys_errmsg_die("%s", optarg); +				if (sb.st_size < 10) +					errmsg_die("%s: not a proper device table file", optarg); +				break; + +			case 'r': +			case 'd':	/* for compatibility with mkfs.jffs, genext2fs, etc... */ +				if (rootdir != default_rootdir) { +					errmsg_die("root directory specified more than once"); +				} +				rootdir = xstrdup(optarg); +				break; + +			case 's': +				page_size = strtol(optarg, NULL, 0); +				warn_page_size = 0; /* set by user, so don't need to warn */ +				break; + +			case 'o': +				if (out_fd != -1) { +					errmsg_die("output filename specified more than once"); +				} +				out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); +				if (out_fd == -1) { +					sys_errmsg_die("open output file"); +				} +				break; + +			case 'q': +				squash_uids = 1; +				squash_perms = 1; +				break; + +			case 'U': +				squash_uids = 1; +				break; + +			case 'P': +				squash_perms = 1; +				break; + +			case 'f': +				fake_times = 1; +				break; + +			case 'h': +			case '?': +				errmsg_die("%s", helptext); + +			case 'v': +				verbose = 1; +				break; + +			case 'V': +				errmsg_die("revision %s\n", revtext); + +			case 'e': { +						  char *next; +						  unsigned units = 0; +						  erase_block_size = strtol(optarg, &next, 0); +						  if (!erase_block_size) +							  errmsg_die("Unrecognisable erase size\n"); + +						  if (*next) { +							  if (!strcmp(next, "KiB")) { +								  units = 1024; +							  } else if (!strcmp(next, "MiB")) { +								  units = 1024 * 1024; +							  } else { +								  errmsg_die("Unknown units in erasesize\n"); +							  } +						  } else { +							  if (erase_block_size < 0x1000) +								  units = 1024; +							  else +								  units = 1; +						  } +						  erase_block_size *= units; + +						  /* If it's less than 8KiB, they're not allowed */ +						  if (erase_block_size < 0x2000) { +							  fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", +									  erase_block_size); +							  erase_block_size = 0x2000; +						  } +						  break; +					  } + +			case 'l': +					  target_endian = __LITTLE_ENDIAN; +					  break; + +			case 'b': +					  target_endian = __BIG_ENDIAN; +					  break; + +			case 'p': +					  if (optarg) +						  pad_fs_size = strtol(optarg, NULL, 0); +					  else +						  pad_fs_size = -1; +					  break; +			case 'n': +					  add_cleanmarkers = 0; +					  break; +			case 'c': +					  cleanmarker_size = strtol(optarg, NULL, 0); +					  if (cleanmarker_size < sizeof(cleanmarker)) { +						  errmsg_die("cleanmarker size must be >= 12"); +					  } +					  if (cleanmarker_size >= erase_block_size) { +						  errmsg_die("cleanmarker size must be < eraseblock size"); +					  } +					  break; +			case 'm': +					  if (jffs2_set_compression_mode_name(optarg)) { +						  errmsg_die("Unknown compression mode %s", optarg); +					  } +					  break; +			case 'x': +					  if (jffs2_disable_compressor_name(optarg)) { +						  errmsg_die("Unknown compressor name %s",optarg); +					  } +					  break; +			case 'X': +					  if (jffs2_enable_compressor_name(optarg)) { +						  errmsg_die("Unknown compressor name %s",optarg); +					  } +					  break; +			case 'L': +					  errmsg_die("\n%s",jffs2_list_compressors()); +					  break; +			case 't': +					  jffs2_compression_check_set(1); +					  break; +			case 'y': +					  compr_name = xmalloc(strlen(optarg)); +					  sscanf(optarg,"%d:%s",&compr_prior,compr_name); +					  if ((compr_prior>=0)&&(compr_name)) { +						  if (jffs2_set_compressor_priority(compr_name, compr_prior)) +							  exit(EXIT_FAILURE); +					  } +					  else { +						  errmsg_die("Cannot parse %s",optarg); +					  } +					  free(compr_name); +					  break; +			case 'i': +					  if (in_fd != -1) { +						  errmsg_die("(incremental) filename specified more than once"); +					  } +					  in_fd = open(optarg, O_RDONLY); +					  if (in_fd == -1) { +						  sys_errmsg_die("cannot open (incremental) file"); +					  } +					  break; +#ifndef WITHOUT_XATTR +			case 1000:	/* --with-xattr  */ +					  enable_xattr |= (1 << JFFS2_XPREFIX_USER) +						  | (1 << JFFS2_XPREFIX_SECURITY) +						  | (1 << JFFS2_XPREFIX_ACL_ACCESS) +						  | (1 << JFFS2_XPREFIX_ACL_DEFAULT) +						  | (1 << JFFS2_XPREFIX_TRUSTED); +					  break; +			case 1001:	/*  --with-selinux  */ +					  enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); +					  break; +			case 1002:	/*  --with-posix-acl  */ +					  enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) +						  | (1 << JFFS2_XPREFIX_ACL_DEFAULT); +					  break; +#endif +		} +	} +	if (warn_page_size) { +		errmsg("Page size for this system is by default %d", page_size); +		errmsg("Use the --pagesize=SIZE option if this is not what you want"); +	} +	if (out_fd == -1) { +		if (isatty(1)) { +			errmsg_die("%s", helptext); +		} +		out_fd = 1; +	} +	if (lstat(rootdir, &sb)) { +		sys_errmsg_die("%s", rootdir); +	} +	if (chdir(rootdir)) +		sys_errmsg_die("%s", rootdir); + +	if (!(cwd = getcwd(0, GETCWD_SIZE))) +		sys_errmsg_die("getcwd failed"); + +	if(in_fd != -1) +		parse_image(); + +	root = recursive_add_host_directory(NULL, "/", cwd); + +	if (devtable) +		parse_device_table(root, devtable); + +	create_target_filesystem(root); + +	cleanup(root); + +	if (rootdir != default_rootdir) +		free(rootdir); + +	close(out_fd); + +	if (verbose) { +		char *s = jffs2_stats(); +		fprintf(stderr,"\n\n%s",s); +		free(s); +	} +	if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) { +		fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get()); +	} + +	jffs2_compressors_exit(); + +	return 0; +} diff --git a/jffsX-utils/rbtree.c b/jffsX-utils/rbtree.c new file mode 100644 index 0000000..329e098 --- /dev/null +++ b/jffsX-utils/rbtree.c @@ -0,0 +1,390 @@ +/* +  Red Black Trees +  (C) 1999  Andrea Arcangeli <andrea@suse.de> +  (C) 2002  David Woodhouse <dwmw2@infradead.org> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + +  linux/lib/rbtree.c +*/ + +#include <stdlib.h> +#include "rbtree.h" + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ +	struct rb_node *right = node->rb_right; +	struct rb_node *parent = rb_parent(node); + +	if ((node->rb_right = right->rb_left)) +		rb_set_parent(right->rb_left, node); +	right->rb_left = node; + +	rb_set_parent(right, parent); + +	if (parent) +	{ +		if (node == parent->rb_left) +			parent->rb_left = right; +		else +			parent->rb_right = right; +	} +	else +		root->rb_node = right; +	rb_set_parent(node, right); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ +	struct rb_node *left = node->rb_left; +	struct rb_node *parent = rb_parent(node); + +	if ((node->rb_left = left->rb_right)) +		rb_set_parent(left->rb_right, node); +	left->rb_right = node; + +	rb_set_parent(left, parent); + +	if (parent) +	{ +		if (node == parent->rb_right) +			parent->rb_right = left; +		else +			parent->rb_left = left; +	} +	else +		root->rb_node = left; +	rb_set_parent(node, left); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ +	struct rb_node *parent, *gparent; + +	while ((parent = rb_parent(node)) && rb_is_red(parent)) +	{ +		gparent = rb_parent(parent); + +		if (parent == gparent->rb_left) +		{ +			{ +				register struct rb_node *uncle = gparent->rb_right; +				if (uncle && rb_is_red(uncle)) +				{ +					rb_set_black(uncle); +					rb_set_black(parent); +					rb_set_red(gparent); +					node = gparent; +					continue; +				} +			} + +			if (parent->rb_right == node) +			{ +				register struct rb_node *tmp; +				__rb_rotate_left(parent, root); +				tmp = parent; +				parent = node; +				node = tmp; +			} + +			rb_set_black(parent); +			rb_set_red(gparent); +			__rb_rotate_right(gparent, root); +		} else { +			{ +				register struct rb_node *uncle = gparent->rb_left; +				if (uncle && rb_is_red(uncle)) +				{ +					rb_set_black(uncle); +					rb_set_black(parent); +					rb_set_red(gparent); +					node = gparent; +					continue; +				} +			} + +			if (parent->rb_left == node) +			{ +				register struct rb_node *tmp; +				__rb_rotate_right(parent, root); +				tmp = parent; +				parent = node; +				node = tmp; +			} + +			rb_set_black(parent); +			rb_set_red(gparent); +			__rb_rotate_left(gparent, root); +		} +	} + +	rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, +			     struct rb_root *root) +{ +	struct rb_node *other; + +	while ((!node || rb_is_black(node)) && node != root->rb_node) +	{ +		if (parent->rb_left == node) +		{ +			other = parent->rb_right; +			if (rb_is_red(other)) +			{ +				rb_set_black(other); +				rb_set_red(parent); +				__rb_rotate_left(parent, root); +				other = parent->rb_right; +			} +			if ((!other->rb_left || rb_is_black(other->rb_left)) && +			    (!other->rb_right || rb_is_black(other->rb_right))) +			{ +				rb_set_red(other); +				node = parent; +				parent = rb_parent(node); +			} +			else +			{ +				if (!other->rb_right || rb_is_black(other->rb_right)) +				{ +					struct rb_node *o_left; +					if ((o_left = other->rb_left)) +						rb_set_black(o_left); +					rb_set_red(other); +					__rb_rotate_right(other, root); +					other = parent->rb_right; +				} +				rb_set_color(other, rb_color(parent)); +				rb_set_black(parent); +				if (other->rb_right) +					rb_set_black(other->rb_right); +				__rb_rotate_left(parent, root); +				node = root->rb_node; +				break; +			} +		} +		else +		{ +			other = parent->rb_left; +			if (rb_is_red(other)) +			{ +				rb_set_black(other); +				rb_set_red(parent); +				__rb_rotate_right(parent, root); +				other = parent->rb_left; +			} +			if ((!other->rb_left || rb_is_black(other->rb_left)) && +			    (!other->rb_right || rb_is_black(other->rb_right))) +			{ +				rb_set_red(other); +				node = parent; +				parent = rb_parent(node); +			} +			else +			{ +				if (!other->rb_left || rb_is_black(other->rb_left)) +				{ +					register struct rb_node *o_right; +					if ((o_right = other->rb_right)) +						rb_set_black(o_right); +					rb_set_red(other); +					__rb_rotate_left(other, root); +					other = parent->rb_left; +				} +				rb_set_color(other, rb_color(parent)); +				rb_set_black(parent); +				if (other->rb_left) +					rb_set_black(other->rb_left); +				__rb_rotate_right(parent, root); +				node = root->rb_node; +				break; +			} +		} +	} +	if (node) +		rb_set_black(node); +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ +	struct rb_node *child, *parent; +	int color; + +	if (!node->rb_left) +		child = node->rb_right; +	else if (!node->rb_right) +		child = node->rb_left; +	else +	{ +		struct rb_node *old = node, *left; + +		node = node->rb_right; +		while ((left = node->rb_left) != NULL) +			node = left; +		child = node->rb_right; +		parent = rb_parent(node); +		color = rb_color(node); + +		if (child) +			rb_set_parent(child, parent); +		if (parent == old) { +			parent->rb_right = child; +			parent = node; +		} else +			parent->rb_left = child; + +		node->rb_parent_color = old->rb_parent_color; +		node->rb_right = old->rb_right; +		node->rb_left = old->rb_left; + +		if (rb_parent(old)) +		{ +			if (rb_parent(old)->rb_left == old) +				rb_parent(old)->rb_left = node; +			else +				rb_parent(old)->rb_right = node; +		} else +			root->rb_node = node; + +		rb_set_parent(old->rb_left, node); +		if (old->rb_right) +			rb_set_parent(old->rb_right, node); +		goto color; +	} + +	parent = rb_parent(node); +	color = rb_color(node); + +	if (child) +		rb_set_parent(child, parent); +	if (parent) +	{ +		if (parent->rb_left == node) +			parent->rb_left = child; +		else +			parent->rb_right = child; +	} +	else +		root->rb_node = child; + + color: +	if (color == RB_BLACK) +		__rb_erase_color(child, parent, root); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ +	struct rb_node	*n; + +	n = root->rb_node; +	if (!n) +		return NULL; +	while (n->rb_left) +		n = n->rb_left; +	return n; +} + +struct rb_node *rb_last(struct rb_root *root) +{ +	struct rb_node	*n; + +	n = root->rb_node; +	if (!n) +		return NULL; +	while (n->rb_right) +		n = n->rb_right; +	return n; +} + +struct rb_node *rb_next(struct rb_node *node) +{ +	struct rb_node *parent; + +	if (rb_parent(node) == node) +		return NULL; + +	/* If we have a right-hand child, go down and then left as far +	   as we can. */ +	if (node->rb_right) { +		node = node->rb_right; +		while (node->rb_left) +			node=node->rb_left; +		return node; +	} + +	/* No right-hand children.  Everything down and left is +	   smaller than us, so any 'next' node must be in the general +	   direction of our parent. Go up the tree; any time the +	   ancestor is a right-hand child of its parent, keep going +	   up. First time it's a left-hand child of its parent, said +	   parent is our 'next' node. */ +	while ((parent = rb_parent(node)) && node == parent->rb_right) +		node = parent; + +	return parent; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ +	struct rb_node *parent; + +	if (rb_parent(node) == node) +		return NULL; + +	/* If we have a left-hand child, go down and then right as far +	   as we can. */ +	if (node->rb_left) { +		node = node->rb_left; +		while (node->rb_right) +			node=node->rb_right; +		return node; +	} + +	/* No left-hand children. Go up till we find an ancestor which +	   is a right-hand child of its parent */ +	while ((parent = rb_parent(node)) && node == parent->rb_left) +		node = parent; + +	return parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, +		     struct rb_root *root) +{ +	struct rb_node *parent = rb_parent(victim); + +	/* Set the surrounding nodes to point to the replacement */ +	if (parent) { +		if (victim == parent->rb_left) +			parent->rb_left = new; +		else +			parent->rb_right = new; +	} else { +		root->rb_node = new; +	} +	if (victim->rb_left) +		rb_set_parent(victim->rb_left, new); +	if (victim->rb_right) +		rb_set_parent(victim->rb_right, new); + +	/* Copy the pointers/colour from the victim to the replacement */ +	*new = *victim; +} diff --git a/jffsX-utils/rbtree.h b/jffsX-utils/rbtree.h new file mode 100644 index 0000000..0d77b65 --- /dev/null +++ b/jffsX-utils/rbtree.h @@ -0,0 +1,171 @@ +/* +  Red Black Trees +  (C) 1999  Andrea Arcangeli <andrea@suse.de> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + +  linux/include/linux/rbtree.h + +  To use rbtrees you'll have to implement your own insert and search cores. +  This will avoid us to use callbacks and to drop drammatically performances. +  I know it's not the cleaner way,  but in C (not in C++) to get +  performances and genericity... + +  Some example of insert and search follows here. The search is a plain +  normal search over an ordered tree. The insert instead must be implemented +  int two steps: as first thing the code must insert the element in +  order as a red leaf in the tree, then the support library function +  rb_insert_color() must be called. Such function will do the +  not trivial work to rebalance the rbtree if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, +						 unsigned long offset) +{ +	struct rb_node * n = inode->i_rb_page_cache.rb_node; +	struct page * page; + +	while (n) +	{ +		page = rb_entry(n, struct page, rb_page_cache); + +		if (offset < page->offset) +			n = n->rb_left; +		else if (offset > page->offset) +			n = n->rb_right; +		else +			return page; +	} +	return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, +						   unsigned long offset, +						   struct rb_node * node) +{ +	struct rb_node ** p = &inode->i_rb_page_cache.rb_node; +	struct rb_node * parent = NULL; +	struct page * page; + +	while (*p) +	{ +		parent = *p; +		page = rb_entry(parent, struct page, rb_page_cache); + +		if (offset < page->offset) +			p = &(*p)->rb_left; +		else if (offset > page->offset) +			p = &(*p)->rb_right; +		else +			return page; +	} + +	rb_link_node(node, parent, p); + +	return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, +						 unsigned long offset, +						 struct rb_node * node) +{ +	struct page * ret; +	if ((ret = __rb_insert_page_cache(inode, offset, node))) +		goto out; +	rb_insert_color(node, &inode->i_rb_page_cache); + out: +	return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef	_LINUX_RBTREE_H +#define	_LINUX_RBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct rb_node +{ +	unsigned long  rb_parent_color; +#define	RB_RED		0 +#define	RB_BLACK	1 +	struct rb_node *rb_right; +	struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); +    /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root +{ +	struct rb_node *rb_node; +}; + + +#define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r)   ((r)->rb_parent_color & 1) +#define rb_is_red(r)   (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ +	rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; +} +static inline void rb_set_color(struct rb_node *rb, int color) +{ +	rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + +#define RB_ROOT	(struct rb_root) { NULL, } + +/* Newer gcc versions take care of exporting this */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({                      \ +        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \ +        (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define	rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root)	((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node)	(rb_parent(node) == node) +#define RB_CLEAR_NODE(node)	(rb_set_parent(node, node)) + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(struct rb_node *); +extern struct rb_node *rb_prev(struct rb_node *); +extern struct rb_node *rb_first(struct rb_root *); +extern struct rb_node *rb_last(struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, +			    struct rb_root *root); + +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, +				struct rb_node ** rb_link) +{ +	node->rb_parent_color = (unsigned long )parent; +	node->rb_left = node->rb_right = NULL; + +	*rb_link = node; +} + +#endif	/* _LINUX_RBTREE_H */ diff --git a/jffsX-utils/summary.h b/jffsX-utils/summary.h new file mode 100644 index 0000000..e9d95a5 --- /dev/null +++ b/jffsX-utils/summary.h @@ -0,0 +1,177 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>, + *                     Zoltan Sogor <weth@inf.u-szeged.hu>, + *                     Patrik Kluba <pajko@halom.u-szeged.hu>, + *                     University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + */ + +#ifndef JFFS2_SUMMARY_H +#define JFFS2_SUMMARY_H + +#include <linux/jffs2.h> + +#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ +	c->free_size -= _x; c->dirty_size += _x; \ +	jeb->free_size -= _x ; jeb->dirty_size += _x; \ +}while(0) +#define USED_SPACE(x) do { typeof(x) _x = (x); \ +	c->free_size -= _x; c->used_size += _x; \ +	jeb->free_size -= _x ; jeb->used_size += _x; \ +}while(0) +#define WASTED_SPACE(x) do { typeof(x) _x = (x); \ +	c->free_size -= _x; c->wasted_size += _x; \ +	jeb->free_size -= _x ; jeb->wasted_size += _x; \ +}while(0) +#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ +	c->free_size -= _x; c->unchecked_size += _x; \ +	jeb->free_size -= _x ; jeb->unchecked_size += _x; \ +}while(0) + +#define BLK_STATE_ALLFF		0 +#define BLK_STATE_CLEAN		1 +#define BLK_STATE_PARTDIRTY	2 +#define BLK_STATE_CLEANMARKER	3 +#define BLK_STATE_ALLDIRTY	4 +#define BLK_STATE_BADBLOCK	5 + +#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff +#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) +#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) + +/* Summary structures used on flash */ + +struct jffs2_sum_unknown_flash +{ +	jint16_t nodetype;	/* node type */ +} __attribute__((packed)); + +struct jffs2_sum_inode_flash +{ +	jint16_t nodetype;	/* node type */ +	jint32_t inode;		/* inode number */ +	jint32_t version;	/* inode version */ +	jint32_t offset;	/* offset on jeb */ +	jint32_t totlen; 	/* record length */ +} __attribute__((packed)); + +struct jffs2_sum_dirent_flash +{ +	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */ +	jint32_t totlen;	/* record length */ +	jint32_t offset;	/* ofset on jeb */ +	jint32_t pino;		/* parent inode */ +	jint32_t version;	/* dirent version */ +	jint32_t ino; 		/* == zero for unlink */ +	uint8_t nsize;		/* dirent name size */ +	uint8_t type;		/* dirent type */ +	uint8_t name[0];	/* dirent name */ +} __attribute__((packed)); + +struct jffs2_sum_xattr_flash +{ +	jint16_t nodetype;	/* == JFFS2_NODETYPE_XATR */ +	jint32_t xid;		/* xattr identifier */ +	jint32_t version;	/* version number */ +	jint32_t offset;	/* offset on jeb */ +	jint32_t totlen;	/* node length */ +} __attribute__((packed)); + +struct jffs2_sum_xref_flash +{ +	jint16_t nodetype;	/* == JFFS2_NODETYPE_XREF */ +	jint32_t offset;	/* offset on jeb */ +} __attribute__((packed)); + +union jffs2_sum_flash +{ +	struct jffs2_sum_unknown_flash u; +	struct jffs2_sum_inode_flash i; +	struct jffs2_sum_dirent_flash d; +	struct jffs2_sum_xattr_flash x; +	struct jffs2_sum_xref_flash r; +}; + +/* Summary structures used in the memory */ + +struct jffs2_sum_unknown_mem +{ +	union jffs2_sum_mem *next; +	jint16_t nodetype;	/* node type */ +} __attribute__((packed)); + +struct jffs2_sum_inode_mem +{ +	union jffs2_sum_mem *next; +	jint16_t nodetype;	/* node type */ +	jint32_t inode;		/* inode number */ +	jint32_t version;	/* inode version */ +	jint32_t offset;	/* offset on jeb */ +	jint32_t totlen; 	/* record length */ +} __attribute__((packed)); + +struct jffs2_sum_dirent_mem +{ +	union jffs2_sum_mem *next; +	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */ +	jint32_t totlen;	/* record length */ +	jint32_t offset;	/* ofset on jeb */ +	jint32_t pino;		/* parent inode */ +	jint32_t version;	/* dirent version */ +	jint32_t ino; 		/* == zero for unlink */ +	uint8_t nsize;		/* dirent name size */ +	uint8_t type;		/* dirent type */ +	uint8_t name[0];	/* dirent name */ +} __attribute__((packed)); + +struct jffs2_sum_xattr_mem +{ +	union jffs2_sum_mem *next; +	jint16_t nodetype; +	jint32_t xid; +	jint32_t version; +	jint32_t offset; +	jint32_t totlen; +} __attribute__((packed)); + +struct jffs2_sum_xref_mem +{ +	union jffs2_sum_mem *next; +	jint16_t nodetype; +	jint32_t offset; +} __attribute__((packed)); + +union jffs2_sum_mem +{ +	struct jffs2_sum_unknown_mem u; +	struct jffs2_sum_inode_mem i; +	struct jffs2_sum_dirent_mem d; +	struct jffs2_sum_xattr_mem x; +	struct jffs2_sum_xref_mem r; +}; + +struct jffs2_summary +{ +	uint32_t sum_size; +	uint32_t sum_num; +	uint32_t sum_padded; +	union jffs2_sum_mem *sum_list_head; +	union jffs2_sum_mem *sum_list_tail; +}; + +/* Summary marker is stored at the end of every sumarized erase block */ + +struct jffs2_sum_marker +{ +	jint32_t offset;	/* offset of the summary node in the jeb */ +	jint32_t magic; 	/* == JFFS2_SUM_MAGIC */ +}; + +#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker)) + +#endif diff --git a/jffsX-utils/sumtool.c b/jffsX-utils/sumtool.c new file mode 100644 index 0000000..886b545 --- /dev/null +++ b/jffsX-utils/sumtool.c @@ -0,0 +1,872 @@ +/* + *  sumtool.c + * + *  Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>, + *                     Ferenc Havasi <havasi@inf.u-szeged.hu> + *                     University of Szeged, Hungary + *                2006 KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * Overview: + *   This is a utility insert summary information into JFFS2 image for + *   faster mount time + * + */ + +#define PROGRAM_NAME "sumtool" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <asm/types.h> +#include <dirent.h> +#include <mtd/jffs2-user.h> +#include <endian.h> +#include <byteswap.h> +#include <getopt.h> +#include <crc32.h> +#include "summary.h" +#include "common.h" + +#define PAD(x) (((x)+3)&~3) + +static struct jffs2_summary *sum_collected = NULL; + +static int verbose = 0; +static int padto = 0;				/* pad the output with 0xFF to the end of the final eraseblock */ +static int add_cleanmarkers = 1;		/* add cleanmarker to output */ +static int use_input_cleanmarker_size = 1;	/* use input file's cleanmarker size (default) */ +static int found_cleanmarkers = 0;		/* cleanmarker found in input file */ +static struct jffs2_unknown_node cleanmarker; +static int cleanmarker_size = sizeof(cleanmarker); +static const char *short_options = "o:i:e:hvVblnc:p"; +static int erase_block_size = 65536; +static int out_fd = -1; +static int in_fd = -1; + +static uint8_t *data_buffer = NULL; 		/* buffer for inodes */ +static unsigned int data_ofs = 0;	 	/* inode buffer offset */ + +static uint8_t *file_buffer = NULL;		/* file buffer contains the actual erase block*/ +static unsigned int file_ofs = 0;		/* position in the buffer */ + +int target_endian = __BYTE_ORDER; + +static struct option long_options[] = { +	{"output", 1, NULL, 'o'}, +	{"input", 1, NULL, 'i'}, +	{"eraseblock", 1, NULL, 'e'}, +	{"help", 0, NULL, 'h'}, +	{"verbose", 0, NULL, 'v'}, +	{"version", 0, NULL, 'V'}, +	{"bigendian", 0, NULL, 'b'}, +	{"littleendian", 0, NULL, 'l'}, +	{"no-cleanmarkers", 0, NULL, 'n'}, +	{"cleanmarker", 1, NULL, 'c'}, +	{"pad", 0, NULL, 'p'}, +	{NULL, 0, NULL, 0} +}; + +static const char helptext[] = +"Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n" +"Convert the input JFFS2 image to a summarized JFFS2 image\n" +"Summary makes mounting faster - if summary support enabled in your kernel\n\n" +"Options:\n" +"  -e, --eraseblock=SIZE     Use erase block size SIZE (default: 64KiB)\n" +"                            (usually 16KiB on NAND)\n" +"  -c, --cleanmarker=SIZE    Size of cleanmarker (default 12).\n" +"                            (usually 16 bytes on NAND, and will be set to\n" +"                            this value if left at the default 12). Will be\n" +"                            stored in OOB after each physical page composing\n" +"                            a physical eraseblock.\n" +"  -n, --no-cleanmarkers     Don't add a cleanmarker to every eraseblock\n" +"  -o, --output=FILE         Output to FILE \n" +"  -i, --input=FILE          Input from FILE \n" +"  -b, --bigendian           Image is big endian\n" +"  -l  --littleendian        Image is little endian\n" +"  -h, --help                Display this help text\n" +"  -v, --verbose             Verbose operation\n" +"  -V, --version             Display version information\n" +"  -p, --pad                 Pad the OUTPUT with 0xFF to the end of the final\n" +"                            eraseblock\n\n"; + + +static const char revtext[] = "$Revision: 1.9 $"; + +static unsigned char ffbuf[16] = { +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static void full_write(void *target_buff, const void *buf, int len); + +void setup_cleanmarker(void) +{ +	cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); +	cleanmarker.totlen = cpu_to_je32(cleanmarker_size); +	cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); +} + +void process_options (int argc, char **argv) +{ +	int opt,c; + +	while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) { +		switch (opt) { +			case 'o': +				if (out_fd != -1) +					errmsg_die("output filename specified more than once"); +				out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); +				if (out_fd == -1) +					sys_errmsg_die("open output file"); +				break; + +			case 'i': +				if (in_fd != -1) +					errmsg_die("input filename specified more than once"); +				in_fd = open(optarg, O_RDONLY); +				if (in_fd == -1) +					sys_errmsg_die("open input file"); +				break; +			case 'b': +				target_endian = __BIG_ENDIAN; +				break; +			case 'l': +				target_endian = __LITTLE_ENDIAN; +				break; +			case 'h': +			case '?': +				errmsg_die("%s", helptext); +			case 'v': +				verbose = 1; +				break; + +			case 'V': +				errmsg_die("revision %.*s\n", +						(int) strlen(revtext) - 13, revtext + 11); + +			case 'e': { +						  char *next; +						  unsigned units = 0; +						  erase_block_size = strtol(optarg, &next, 0); +						  if (!erase_block_size) +							  errmsg_die("Unrecognisable erase size\n"); + +						  if (*next) { +							  if (!strcmp(next, "KiB")) { +								  units = 1024; +							  } else if (!strcmp(next, "MiB")) { +								  units = 1024 * 1024; +							  } else { +								  errmsg_die("Unknown units in erasesize\n"); +							  } +						  } else { +							  if (erase_block_size < 0x1000) +								  units = 1024; +							  else +								  units = 1; +						  } +						  erase_block_size *= units; + +						  /* If it's less than 8KiB, they're not allowed */ +						  if (erase_block_size < 0x2000) { +							  warnmsg("Erase size 0x%x too small. Increasing to 8KiB minimum\n", +									erase_block_size); +							  erase_block_size = 0x2000; +						  } +						  break; +					  } + +			case 'n': +					  add_cleanmarkers = 0; +					  break; +			case 'c': +					  cleanmarker_size = strtol(optarg, NULL, 0); + +					  if (cleanmarker_size < sizeof(cleanmarker)) { +						  errmsg_die("cleanmarker size must be >= 12"); +					  } +					  if (cleanmarker_size >= erase_block_size) { +						  errmsg_die("cleanmarker size must be < eraseblock size"); +					  } + +					  use_input_cleanmarker_size = 0; +					  found_cleanmarkers = 1; +					  setup_cleanmarker(); + +					  break; +			case 'p': +					  padto = 1; +					  break; +		} +	} +} + + +void init_buffers(void) +{ +	data_buffer = xmalloc(erase_block_size); +	file_buffer = xmalloc(erase_block_size); +} + +void init_sumlist(void) +{ +	sum_collected = xzalloc(sizeof(*sum_collected)); +} + +void clean_buffers(void) +{ +	free(data_buffer); +	free(file_buffer); +} + +void clean_sumlist(void) +{ +	union jffs2_sum_mem *temp; + +	if (sum_collected) { + +		while (sum_collected->sum_list_head) { +			temp = sum_collected->sum_list_head; +			sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; +			free(temp); +			sum_collected->sum_num--; +		} + +		if (sum_collected->sum_num != 0) +			warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); + +		free(sum_collected); +	} +} + +int load_next_block(void) +{ +	int ret; +	ret = read(in_fd, file_buffer, erase_block_size); +	file_ofs = 0; + +	bareverbose(verbose, "Load next block : %d bytes read\n", ret); + +	return ret; +} + +void write_buff_to_file(void) +{ +	int ret; +	int len = data_ofs; + +	uint8_t *buf = NULL; + +	buf = data_buffer; +	while (len > 0) { +		ret = write(out_fd, buf, len); + +		if (ret < 0) +			sys_errmsg_die("write"); + +		if (ret == 0) +			sys_errmsg_die("write returned zero"); + +		len -= ret; +		buf += ret; +	} + +	data_ofs = 0; +} + +void dump_sum_records(void) +{ + +	struct jffs2_raw_summary isum; +	struct jffs2_sum_marker *sm; +	union jffs2_sum_mem *temp; +	jint32_t offset; +	jint32_t *tpage; +	void *wpage; +	int datasize, infosize, padsize; +	jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC); + +	if (!sum_collected->sum_num || !sum_collected->sum_list_head) +		return; + +	datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker); +	infosize = sizeof(struct jffs2_raw_summary) + datasize; +	padsize = erase_block_size - data_ofs - infosize; +	infosize += padsize; datasize += padsize; +	offset = cpu_to_je32(data_ofs); + +	tpage = xmalloc(datasize); + +	memset(tpage, 0xff, datasize); +	memset(&isum, 0, sizeof(isum)); + +	isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); +	isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); +	isum.totlen = cpu_to_je32(infosize); +	isum.hdr_crc = cpu_to_je32(mtd_crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); +	isum.padded = cpu_to_je32(0); + +	if (add_cleanmarkers && found_cleanmarkers) { +		isum.cln_mkr = cpu_to_je32(cleanmarker_size); +	} else { +		isum.cln_mkr = cpu_to_je32(0); +	} + +	isum.sum_num = cpu_to_je32(sum_collected->sum_num); +	wpage = tpage; + +	while (sum_collected->sum_num) { +		switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) { + +			case JFFS2_NODETYPE_INODE : { +											struct jffs2_sum_inode_flash *sino_ptr = wpage; + +											sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype; +											sino_ptr->inode = sum_collected->sum_list_head->i.inode; +											sino_ptr->version = sum_collected->sum_list_head->i.version; +											sino_ptr->offset = sum_collected->sum_list_head->i.offset; +											sino_ptr->totlen = sum_collected->sum_list_head->i.totlen; + +											wpage += JFFS2_SUMMARY_INODE_SIZE; +											break; +										} + +			case JFFS2_NODETYPE_DIRENT : { +											 struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; + +											 sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype; +											 sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen; +											 sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset; +											 sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino; +											 sdrnt_ptr->version = sum_collected->sum_list_head->d.version; +											 sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino; +											 sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize; +											 sdrnt_ptr->type = sum_collected->sum_list_head->d.type; + +											 memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name, +													 sum_collected->sum_list_head->d.nsize); + +											 wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize); +											 break; +										 } + +			case JFFS2_NODETYPE_XATTR: { +										   struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; + +										   sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; +										   sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; +										   sxattr_ptr->version = sum_collected->sum_list_head->x.version; +										   sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; +										   sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; + +										   wpage += JFFS2_SUMMARY_XATTR_SIZE; +										   break; +									   } + +			case JFFS2_NODETYPE_XREF: { +										  struct jffs2_sum_xref_flash *sxref_ptr = wpage; + +										  sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; +										  sxref_ptr->offset = sum_collected->sum_list_head->r.offset; + +										  wpage += JFFS2_SUMMARY_XREF_SIZE; +										  break; +									  } + +			default : { +						  warnmsg("Unknown node type!\n"); +					  } +		} + +		temp = sum_collected->sum_list_head; +		sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; +		free(temp); + +		sum_collected->sum_num--; +	} + +	sum_collected->sum_size = 0; +	sum_collected->sum_num = 0; +	sum_collected->sum_list_tail = NULL; + +	wpage += padsize; + +	sm = wpage; +	sm->offset = offset; +	sm->magic = magic; + +	isum.sum_crc = cpu_to_je32(mtd_crc32(0, tpage, datasize)); +	isum.node_crc = cpu_to_je32(mtd_crc32(0, &isum, sizeof(isum) - 8)); + +	full_write(data_buffer + data_ofs, &isum, sizeof(isum)); +	full_write(data_buffer + data_ofs, tpage, datasize); + +	free(tpage); +} + +static void full_write(void *target_buff, const void *buf, int len) +{ +	memcpy(target_buff, buf, len); +	data_ofs += len; +} + +static void pad(int req) +{ +	while (req) { +		if (req > sizeof(ffbuf)) { +			full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf)); +			req -= sizeof(ffbuf); +		} else { +			full_write(data_buffer + data_ofs, ffbuf, req); +			req = 0; +		} +	} +} + +static inline void padword(void) +{ +	if (data_ofs % 4) +		full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4)); +} + + +static inline void pad_block_if_less_than(int req,int plus) +{ + +	int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; +	datasize += (4 - (datasize % 4)) % 4; + +	if (data_ofs + req > erase_block_size - datasize) { +		dump_sum_records(); +		write_buff_to_file(); +	} + +	if (add_cleanmarkers && found_cleanmarkers) { +		if (!data_ofs) { +			full_write(data_buffer, &cleanmarker, sizeof(cleanmarker)); +			pad(cleanmarker_size - sizeof(cleanmarker)); +			padword(); +		} +	} +} + +void flush_buffers(void) +{ + +	if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */ +		if (data_ofs != cleanmarker_size) {	/* INODE BUFFER */ + +			int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; +			datasize += (4 - (datasize % 4)) % 4; + +			/* If we have a full inode buffer, then write out inode and summary data  */ +			if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { +				dump_sum_records(); +				write_buff_to_file(); +			} else {	/* else just write out inode data */ +				if (padto) +					pad(erase_block_size - data_ofs); +				write_buff_to_file(); +			} +		} +	} else { /* NO CLEANMARKER */ +		if (data_ofs != 0) { /* INODE BUFFER */ + +			int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; +			datasize += (4 - (datasize % 4)) % 4; + +			/* If we have a full inode buffer, then write out inode and summary data */ +			if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { +				dump_sum_records(); +				write_buff_to_file(); +			} else {	/* Else just write out inode data */ +				if(padto) +					pad(erase_block_size - data_ofs); +				write_buff_to_file(); +			} +		} +	} +} + +int add_sum_mem(union jffs2_sum_mem *item) +{ + +	if (!sum_collected->sum_list_head) +		sum_collected->sum_list_head = (union jffs2_sum_mem *) item; +	if (sum_collected->sum_list_tail) +		sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item; +	sum_collected->sum_list_tail = (union jffs2_sum_mem *) item; + +	switch (je16_to_cpu(item->u.nodetype)) { +		case JFFS2_NODETYPE_INODE: +			sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE; +			sum_collected->sum_num++; +			break; + +		case JFFS2_NODETYPE_DIRENT: +			sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); +			sum_collected->sum_num++; +			break; + +		case JFFS2_NODETYPE_XATTR: +			sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; +			sum_collected->sum_num++; +			break; + +		case JFFS2_NODETYPE_XREF: +			sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; +			sum_collected->sum_num++; +			break; + +		default: +			errmsg_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype)); +	} +	return 0; +} + +void add_sum_inode_mem(union jffs2_node_union *node) +{ +	struct jffs2_sum_inode_mem *temp = xmalloc(sizeof(*temp)); + +	temp->nodetype = node->i.nodetype; +	temp->inode = node->i.ino; +	temp->version = node->i.version; +	temp->offset = cpu_to_je32(data_ofs); +	temp->totlen = node->i.totlen; +	temp->next = NULL; + +	add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_dirent_mem(union jffs2_node_union *node) +{ +	struct jffs2_sum_dirent_mem *temp = xmalloc(sizeof(*temp) + node->d.nsize); + +	temp->nodetype = node->d.nodetype; +	temp->totlen = node->d.totlen; +	temp->offset = cpu_to_je32(data_ofs); +	temp->pino = node->d.pino; +	temp->version = node->d.version; +	temp->ino = node->d.ino; +	temp->nsize = node->d.nsize; +	temp->type = node->d.type; +	temp->next = NULL; + +	memcpy(temp->name,node->d.name,node->d.nsize); +	add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_xattr_mem(union jffs2_node_union *node) +{ +	struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); + +	temp->nodetype = node->x.nodetype; +	temp->xid = node->x.xid; +	temp->version = node->x.version; +	temp->offset = cpu_to_je32(data_ofs); +	temp->totlen = node->x.totlen; +	temp->next = NULL; + +	add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_xref_mem(union jffs2_node_union *node) +{ +	struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); + +	temp->nodetype = node->r.nodetype; +	temp->offset = cpu_to_je32(data_ofs); +	temp->next = NULL; + +	add_sum_mem((union jffs2_sum_mem *) temp); +} + +void write_dirent_to_buff(union jffs2_node_union *node) +{ +	pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize)); +	add_sum_dirent_mem(node); +	full_write(data_buffer + data_ofs, &(node->d), je32_to_cpu (node->d.totlen)); +	padword(); +} + + +void write_inode_to_buff(union jffs2_node_union *node) +{ +	pad_block_if_less_than(je32_to_cpu (node->i.totlen),JFFS2_SUMMARY_INODE_SIZE); +	add_sum_inode_mem(node);	/* Add inode summary mem to summary list */ +	full_write(data_buffer + data_ofs, &(node->i), je32_to_cpu (node->i.totlen));	/* Write out the inode to inode_buffer */ +	padword(); +} + +void write_xattr_to_buff(union jffs2_node_union *node) +{ +	pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); +	add_sum_xattr_mem(node);	/* Add xdatum summary mem to summary list */ +	full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); +	padword(); +} + +void write_xref_to_buff(union jffs2_node_union *node) +{ +	pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); +	add_sum_xref_mem(node);		/* Add xref summary mem to summary list */ +	full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); +	padword(); +} + +void create_summed_image(int inp_size) +{ +	uint8_t *p = file_buffer; +	union jffs2_node_union *node; +	uint32_t crc, length; +	uint16_t type; +	int bitchbitmask = 0; +	int obsolete; +	char name[256]; + +	while ( p < (file_buffer + inp_size)) { + +		node = (union jffs2_node_union *) p; + +		/* Skip empty space */ +		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { +			p += 4; +			continue; +		} + +		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { +			if (!bitchbitmask++) +				warnmsg("Wrong bitmask  at  0x%08zx, 0x%04x\n", +					p - file_buffer, je16_to_cpu (node->u.magic)); +			p += 4; +			continue; +		} + +		bitchbitmask = 0; + +		type = je16_to_cpu(node->u.nodetype); +		if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { +			obsolete = 1; +			type |= JFFS2_NODE_ACCURATE; +		} else { +			obsolete = 0; +		} + +		node->u.nodetype = cpu_to_je16(type); + +		crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); +		if (crc != je32_to_cpu (node->u.hdr_crc)) { +			warnmsg("Wrong hdr_crc  at  0x%08zx, 0x%08x instead of 0x%08x\n", +				p - file_buffer, je32_to_cpu (node->u.hdr_crc), crc); +			p += 4; +			continue; +		} + +		switch(je16_to_cpu(node->u.nodetype)) { +			case JFFS2_NODETYPE_INODE: +				bareverbose(verbose, +					"%8s Inode      node at 0x%08zx, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), +					je32_to_cpu (node->i.version), je32_to_cpu (node->i.isize), +					je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); + +				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); +				if (crc != je32_to_cpu (node->i.node_crc)) { +					warnmsg("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", +						p - file_buffer, je32_to_cpu (node->i.node_crc), crc); +					p += PAD(je32_to_cpu (node->i.totlen)); +					continue; +				} + +				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); +				if (crc != je32_to_cpu(node->i.data_crc)) { +					warnmsg("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", +						p - file_buffer, je32_to_cpu (node->i.data_crc), crc); +					p += PAD(je32_to_cpu (node->i.totlen)); +					continue; +				} + +				write_inode_to_buff(node); + +				p += PAD(je32_to_cpu (node->i.totlen)); +				break; + +			case JFFS2_NODETYPE_DIRENT: +				memcpy (name, node->d.name, node->d.nsize); +				name [node->d.nsize] = 0x0; + +				bareverbose(verbose, +					"%8s Dirent     node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), +					je32_to_cpu (node->d.version), je32_to_cpu (node->d.ino), +					node->d.nsize, name); + +				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); +				if (crc != je32_to_cpu (node->d.node_crc)) { +					warnmsg("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", +						p - file_buffer, je32_to_cpu (node->d.node_crc), crc); +					p += PAD(je32_to_cpu (node->d.totlen)); +					continue; +				} + +				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); +				if (crc != je32_to_cpu(node->d.name_crc)) { +					warnmsg("Wrong name_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", +						p - file_buffer, je32_to_cpu (node->d.name_crc), crc); +					p += PAD(je32_to_cpu (node->d.totlen)); +					continue; +				} + +				write_dirent_to_buff(node); + +				p += PAD(je32_to_cpu (node->d.totlen)); +				break; + +			case JFFS2_NODETYPE_XATTR: +				if (je32_to_cpu(node->x.node_crc) == 0xffffffff) +					obsolete = 1; +				bareverbose(verbose, +					"%8s Xdatum     node at 0x%08zx, totlen 0x%08x, #xid  %5u, version %5u\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->x.totlen), +					je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); +				crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); +				if (crc != je32_to_cpu(node->x.node_crc)) { +					warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", +							p - file_buffer, je32_to_cpu(node->x.node_crc), crc); +					p += PAD(je32_to_cpu (node->x.totlen)); +					continue; +				} +				length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); +				crc = mtd_crc32(0, node->x.data, length); +				if (crc != je32_to_cpu(node->x.data_crc)) { +					warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", +							p - file_buffer, je32_to_cpu(node->x.data_crc), crc); +					p += PAD(je32_to_cpu (node->x.totlen)); +					continue; +				} + +				write_xattr_to_buff(node); +				p += PAD(je32_to_cpu (node->x.totlen)); +				break; + +			case JFFS2_NODETYPE_XREF: +				if (je32_to_cpu(node->r.node_crc) == 0xffffffff) +					obsolete = 1; +				bareverbose(verbose, +					"%8s Xref       node at 0x%08zx, totlen 0x%08x, #ino  %5u, xid     %5u\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu(node->r.totlen), +					je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); +				crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); +				if (crc != je32_to_cpu(node->r.node_crc)) { +					warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", +							p - file_buffer, je32_to_cpu(node->r.node_crc), crc); +					p += PAD(je32_to_cpu (node->r.totlen)); +					continue; +				} + +				write_xref_to_buff(node); +				p += PAD(je32_to_cpu (node->r.totlen)); +				break; + +			case JFFS2_NODETYPE_CLEANMARKER: +				bareverbose(verbose, +					"%8s Cleanmarker     at 0x%08zx, totlen 0x%08x\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->u.totlen)); + +				if (!found_cleanmarkers) { +					found_cleanmarkers = 1; + +					if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){ +						cleanmarker_size = je32_to_cpu (node->u.totlen); +						setup_cleanmarker(); +					} +				} + +				p += PAD(je32_to_cpu (node->u.totlen)); +				break; + +			case JFFS2_NODETYPE_PADDING: +				bareverbose(verbose, +					"%8s Padding    node at 0x%08zx, totlen 0x%08x\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->u.totlen)); +				p += PAD(je32_to_cpu (node->u.totlen)); +				break; + +			case 0xffff: +				p += 4; +				break; + +			default: +				bareverbose(verbose, +					"%8s Unknown    node at 0x%08zx, totlen 0x%08x\n", +					obsolete ? "Obsolete" : "", +					p - file_buffer, je32_to_cpu (node->u.totlen)); + +				p += PAD(je32_to_cpu (node->u.totlen)); +		} +	} +} + +int main(int argc, char **argv) +{ +	int ret; + +	process_options(argc,argv); + +	if ((in_fd == -1) || (out_fd == -1)) { +		if(in_fd != -1) +			close(in_fd); +		if(out_fd != -1) +			close(out_fd); +		fprintf(stderr, "%s", helptext); +		errmsg_die("You must specify input and output files!\n"); +	} + +	init_buffers(); +	init_sumlist(); + +	while ((ret = load_next_block())) { +		create_summed_image(ret); +	} + +	flush_buffers(); +	clean_buffers(); +	clean_sumlist(); + +	if (in_fd != -1) +		close(in_fd); +	if (out_fd != -1) +		close(out_fd); + +	return 0; +} | 
