diff options
Diffstat (limited to 'fs/jffs2')
-rwxr-xr-x | fs/jffs2/Makefile | 48 | ||||
-rwxr-xr-x | fs/jffs2/compr_lzari.c | 262 | ||||
-rwxr-xr-x | fs/jffs2/compr_lzo.c | 405 | ||||
-rwxr-xr-x | fs/jffs2/compr_rtime.c | 91 | ||||
-rwxr-xr-x | fs/jffs2/compr_rubin.c | 126 | ||||
-rwxr-xr-x | fs/jffs2/compr_zlib.c | 52 | ||||
-rwxr-xr-x | fs/jffs2/jffs2_1pass.c | 1370 | ||||
-rwxr-xr-x | fs/jffs2/jffs2_private.h | 88 | ||||
-rwxr-xr-x | fs/jffs2/mini_inflate.c | 396 |
9 files changed, 2838 insertions, 0 deletions
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile new file mode 100755 index 0000000..f28b17a --- /dev/null +++ b/fs/jffs2/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000, 2001 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 +# + +include $(TOPDIR)/config.mk + +LIB = libjffs2.a + +AOBJS = +COBJS = jffs2_1pass.o compr_rtime.o compr_rubin.o compr_zlib.o mini_inflate.o +COBJS += compr_lzo.o compr_lzari.o +OBJS = $(AOBJS) $(COBJS) + +#CPPFLAGS += + +all: $(LIB) $(AOBJS) + +$(LIB): .depend $(OBJS) + $(AR) crv $@ $(OBJS) + + +######################################################################### + +.depend: Makefile $(AOBJS:.o=.S) $(COBJS:.o=.c) + $(CC) -M $(CFLAGS) $(AOBJS:.o=.S) $(COBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/fs/jffs2/compr_lzari.c b/fs/jffs2/compr_lzari.c new file mode 100755 index 0000000..828b6e5 --- /dev/null +++ b/fs/jffs2/compr_lzari.c @@ -0,0 +1,262 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Patrik Kluba, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr_lzari.c,v 1.3 2004/06/23 16:34:39 havasi Exp $ + * + */ + +/* + Lempel-Ziv-Arithmetic coding compression module for jffs2 + Based on the LZARI source included in LDS (lossless datacompression sources) +*/ + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ + +/* +Original copyright follows: + +************************************************************** + LZARI.C -- A Data Compression Program + (tab = 4 spaces) +************************************************************** + 4/7/1989 Haruhiko Okumura + Use, distribute, and modify this program freely. + Please send me your improved versions. + PC-VAN SCIENCE + NIFTY-Serve PAF01022 + CompuServe 74050,1022 +************************************************************** + +LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake. +All rights reserved. Permission granted for non-commercial use. + +*/ + +/* + + 2004-02-18 pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu> + Removed unused variables and fixed no return value + + 2004-02-16 pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu> + Initial release + +*/ + + +#include <config.h> +#if ((CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_LZO_LZARI)) + +#include <linux/stddef.h> +#include <jffs2/jffs2.h> + + +#define N 4096 /* size of ring buffer */ +#define F 60 /* upper limit for match_length */ +#define THRESHOLD 2 /* encode string into position and length + if match_length is greater than this */ +#define NIL N /* index for root of binary search trees */ + +static unsigned char + text_buf[N + F - 1]; /* ring buffer of size N, + with extra F-1 bytes to facilitate string comparison */ + +/********** Arithmetic Compression **********/ + +/* If you are not familiar with arithmetic compression, you should read + I. E. Witten, R. M. Neal, and J. G. Cleary, + Communications of the ACM, Vol. 30, pp. 520-540 (1987), + from which much have been borrowed. */ + +#define M 15 + +/* Q1 (= 2 to the M) must be sufficiently large, but not so + large as the unsigned long 4 * Q1 * (Q1 - 1) overflows. */ + +#define Q1 (1UL << M) +#define Q2 (2 * Q1) +#define Q3 (3 * Q1) +#define Q4 (4 * Q1) +#define MAX_CUM (Q1 - 1) + +#define N_CHAR (256 - THRESHOLD + F) + /* character code = 0, 1, ..., N_CHAR - 1 */ + +static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1]; +static unsigned long + sym_freq[N_CHAR + 1], /* frequency for symbols */ + sym_cum[N_CHAR + 1], /* cumulative freq for symbols */ + position_cum[N + 1]; /* cumulative freq for positions */ + +static void StartModel(void) /* Initialize model */ +{ + unsigned long ch, sym, i; + + sym_cum[N_CHAR] = 0; + for (sym = N_CHAR; sym >= 1; sym--) { + ch = sym - 1; + char_to_sym[ch] = sym; sym_to_char[sym] = ch; + sym_freq[sym] = 1; + sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym]; + } + sym_freq[0] = 0; /* sentinel (!= sym_freq[1]) */ + position_cum[N] = 0; + for (i = N; i >= 1; i--) + position_cum[i - 1] = position_cum[i] + 10000 / (i + 200); + /* empirical distribution function (quite tentative) */ + /* Please devise a better mechanism! */ +} + +static void UpdateModel(unsigned long sym) +{ + unsigned long c, ch_i, ch_sym; + unsigned long i; + if (sym_cum[0] >= MAX_CUM) { + c = 0; + for (i = N_CHAR; i > 0; i--) { + sym_cum[i] = c; + c += (sym_freq[i] = (sym_freq[i] + 1) >> 1); + } + sym_cum[0] = c; + } + for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ; + if (i < sym) { + ch_i = sym_to_char[i]; ch_sym = sym_to_char[sym]; + sym_to_char[i] = ch_sym; sym_to_char[sym] = ch_i; + char_to_sym[ch_i] = sym; char_to_sym[ch_sym] = i; + } + sym_freq[i]++; + while (--i > 0) sym_cum[i]++; + sym_cum[0]++; +} + +static unsigned long BinarySearchSym(unsigned long x) + /* 1 if x >= sym_cum[1], + N_CHAR if sym_cum[N_CHAR] > x, + i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */ +{ + unsigned long i, j, k; + + i = 1; j = N_CHAR; + while (i < j) { + k = (i + j) / 2; + if (sym_cum[k] > x) i = k + 1; else j = k; + } + return i; +} + +unsigned long BinarySearchPos(unsigned long x) + /* 0 if x >= position_cum[1], + N - 1 if position_cum[N] > x, + i such that position_cum[i] > x >= position_cum[i + 1] otherwise */ +{ + unsigned long i, j, k; + + i = 1; j = N; + while (i < j) { + k = (i + j) / 2; + if (position_cum[k] > x) i = k + 1; else j = k; + } + return i - 1; +} + +static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen, + unsigned long dstlen) /* Just the reverse of Encode(). */ +{ + unsigned long i, r, j, k, c, range, sym; + unsigned char *ip, *op; + unsigned char *srcend = srcbuf + srclen; + unsigned char *dstend = dstbuf + dstlen; + unsigned char buffer = 0; + unsigned char mask = 0; + unsigned long low = 0; + unsigned long high = Q4; + unsigned long value = 0; + + ip = srcbuf; + op = dstbuf; + for (i = 0; i < M + 2; i++) { + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + + StartModel(); + for (i = 0; i < N - F; i++) text_buf[i] = ' '; + r = N - F; + + while (op < dstend) { + range = high - low; + sym = BinarySearchSym((unsigned long) + (((value - low + 1) * sym_cum[0] - 1) / range)); + high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; + low += (range * sym_cum[sym ]) / sym_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + c = sym_to_char[sym]; + UpdateModel(sym); + if (c < 256) { + if (op >= dstend) return -1; + *(op++) = c; + text_buf[r++] = c; + r &= (N - 1); + } else { + j = c - 255 + THRESHOLD; + range = high - low; + i = BinarySearchPos((unsigned long) + (((value - low + 1) * position_cum[0] - 1) / range)); + high = low + (range * position_cum[i ]) / position_cum[0]; + low += (range * position_cum[i + 1]) / position_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + i = (r - i - 1) & (N - 1); + for (k = 0; k < j; k++) { + c = text_buf[(i + k) & (N - 1)]; + if (op >= dstend) return -1; + *(op++) = c; + text_buf[r++] = c; + r &= (N - 1); + } + } + } + return 0; +} + +int lzari_decompress(unsigned char *data_in, unsigned char *cpage_out, + u32 srclen, u32 destlen) +{ + return Decode(data_in, cpage_out, srclen, destlen); +} +#endif /* ((CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_LZO_LZARI)) */ diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c new file mode 100755 index 0000000..b6c590a --- /dev/null +++ b/fs/jffs2/compr_lzo.c @@ -0,0 +1,405 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Patrik Kluba, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr_lzo.c,v 1.3 2004/06/23 16:34:39 havasi Exp $ + * + */ + +/* + LZO1X-1 (and -999) compression module for jffs2 + based on the original LZO sources +*/ + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ + +/* + Original copyright notice follows: + + lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm + lzo_ptr.h -- low-level pointer constructs + lzo_swd.ch -- sliding window dictionary + lzoconf.h -- configuration for the LZO real-time data compression library + lzo_mchw.ch -- matching functions using a window + minilzo.c -- mini subset of the LZO real-time data compression library + config1x.h -- configuration for the LZO1X algorithm + lzo1x.h -- public interface of the LZO1X compression algorithm + + These files are part of the LZO real-time data compression library. + + Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library 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. + + The LZO library 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 the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + <markus@oberhumer.com> +*/ + +/* + + 2004-02-16 pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu> + Initial release + -removed all 16 bit code + -all sensitive data will be on 4 byte boundary + -removed check parts for library use + -removed all but LZO1X-* compression + +*/ + + +#include <config.h> +#if ((CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_LZO_LZARI)) + +#include <linux/stddef.h> +#include <jffs2/jffs2.h> +#include <jffs2/compr_rubin.h> + +/* Integral types that have *exactly* the same number of bits as a lzo_voidp */ +typedef unsigned long lzo_ptr_t; +typedef long lzo_sptr_t; + +/* data type definitions */ +#define U32 unsigned long +#define S32 signed long +#define I32 long +#define U16 unsigned short +#define S16 signed short +#define I16 short +#define U8 unsigned char +#define S8 signed char +#define I8 char + +#define M1_MAX_OFFSET 0x0400 +#define M2_MAX_OFFSET 0x0800 +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src) +#define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src)) + +#define TEST_IP (ip < ip_end) +#define TEST_OP (op <= op_end) + +#define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +#define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +#define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun + +typedef U32 lzo_uint32; +typedef I32 lzo_int32; +typedef U32 lzo_uint; +typedef I32 lzo_int; +typedef int lzo_bool; + +#define lzo_byte U8 +#define lzo_bytep U8 * +#define lzo_charp char * +#define lzo_voidp void * +#define lzo_shortp short * +#define lzo_ushortp unsigned short * +#define lzo_uint32p lzo_uint32 * +#define lzo_int32p lzo_int32 * +#define lzo_uintp lzo_uint * +#define lzo_intp lzo_int * +#define lzo_voidpp lzo_voidp * +#define lzo_bytepp lzo_bytep * +#define lzo_sizeof_dict_t sizeof(lzo_bytep) + +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_OUT_OF_MEMORY (-2) /* not used right now */ +#define LZO_E_NOT_COMPRESSIBLE (-3) /* not used right now */ +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_EOF_NOT_FOUND (-7) +#define LZO_E_INPUT_NOT_CONSUMED (-8) + +#define PTR(a) ((lzo_ptr_t) (a)) +#define PTR_LINEAR(a) PTR(a) +#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0) +#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0) +#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0) +#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0) +#define PTR_LT(a,b) (PTR(a) < PTR(b)) +#define PTR_GE(a,b) (PTR(a) >= PTR(b)) +#define PTR_DIFF(a,b) ((lzo_ptrdiff_t) (PTR(a) - PTR(b))) +#define pd(a,b) ((lzo_uint) ((a)-(b))) + +typedef ptrdiff_t lzo_ptrdiff_t; + +static int +lzo1x_decompress (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; + + register const lzo_byte *m_pos; + + const lzo_byte *const ip_end = in + in_len; + lzo_byte *const op_end = out + *out_len; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + NEED_OP (t); + NEED_IP (t + 1); + do + *op++ = *ip++; + while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 15 + *ip++; + } + NEED_OP (t + 3); + NEED_IP (t + 4); + if (PTR_ALIGNED2_4 (op, ip)) + { + COPY4 (op, ip); + + op += 4; + ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do + { + COPY4 (op, ip); + op += 4; + ip += 4; + t -= 4; + } + while (t >= 4); + if (t > 0) + do + *op++ = *ip++; + while (--t > 0); + } + else + do + *op++ = *ip++; + while (--t > 0); + } + } + else + { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + do + *op++ = *ip++; + while (--t > 0); + } + first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; + + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (3); + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos; + + goto match_done; + + while (TEST_IP && TEST_OP) + { + match: + if (t >= 64) + { + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (t + 3 - 1); + goto copy_match; + + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 31 + *ip++; + } + + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); + + ip += 2; + } + else if (t >= 16) + { + m_pos = op; + m_pos -= (t & 8) << 11; + + t &= 7; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 7 + *ip++; + } + + m_pos -= (ip[0] >> 2) + (ip[1] << 6); + + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + else + { + + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (2); + *op++ = *m_pos++; + *op++ = *m_pos; + + goto match_done; + } + + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (t + 3 - 1); + if (t >= 2 * 4 - (3 - 1) + && PTR_ALIGNED2_4 (op, m_pos)) + { + COPY4 (op, m_pos); + op += 4; + m_pos += 4; + t -= 4 - (3 - 1); + do + { + COPY4 (op, m_pos); + op += 4; + m_pos += 4; + t -= 4; + } + while (t >= 4); + if (t > 0) + do + *op++ = *m_pos++; + while (--t > 0); + } + else + + { + copy_match: + *op++ = *m_pos++; + *op++ = *m_pos++; + do + *op++ = *m_pos++; + while (--t > 0); + } + + match_done: + t = ip[-2] & 3; + + if (t == 0) + break; + + match_next: + NEED_OP (t); + NEED_IP (t + 1); + do + *op++ = *ip++; + while (--t > 0); + t = *ip++; + } + } + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; + + eof_found: + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < + ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + + input_overrun: + *out_len = op - out; + return LZO_E_INPUT_OVERRUN; + + output_overrun: + *out_len = op - out; + return LZO_E_OUTPUT_OVERRUN; + + lookbehind_overrun: + *out_len = op - out; + return LZO_E_LOOKBEHIND_OVERRUN; +} + +int lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, + u32 srclen, u32 destlen) +{ + lzo_uint outlen = destlen; + return lzo1x_decompress (data_in, srclen, cpage_out, &outlen, NULL); +} + +#endif /* ((CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_LZO_LZARI)) */ diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c new file mode 100755 index 0000000..9bb4f1b --- /dev/null +++ b/fs/jffs2/compr_rtime.c @@ -0,0 +1,91 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@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. + * + * $Id: compr_rtime.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $ + * + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last + * occurances" 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 <config.h> +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/jffs2.h> + +void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, + u32 srclen, u32 destlen) +{ + int positions[256]; + int outpos; + int pos; + int i; + + outpos = pos = 0; + + for (i = 0; i < 256; positions[i++] = 0); + + 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 { + for (i = 0; i < repeat; i++) + *(cpage_out + outpos + i) = *(cpage_out + backoffs + i); + outpos+=repeat; + } + } + } +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c new file mode 100755 index 0000000..74577d9 --- /dev/null +++ b/fs/jffs2/compr_rubin.c @@ -0,0 +1,126 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@redhat.com> + * + * Heavily modified by Russ Dill <Russ.Dill@asu.edu> in an attempt at + * a little more speed. + * + * 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. + * + * $Id: compr_rubin.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $ + * + */ + +#include <config.h> +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/jffs2.h> +#include <jffs2/compr_rubin.h> + + +void rubin_do_decompress(unsigned char *bits, unsigned char *in, + unsigned char *page_out, __u32 destlen) +{ + register char *curr = (char *)page_out; + char *end = (char *)(page_out + destlen); + register unsigned long temp; + register unsigned long result; + register unsigned long p; + register unsigned long q; + register unsigned long rec_q; + register unsigned long bit; + register long i0; + unsigned long i; + + /* init_pushpull */ + temp = *(u32 *) in; + bit = 16; + + /* init_rubin */ + q = 0; + p = (long) (2 * UPPER_BIT_RUBIN); + + /* init_decode */ + rec_q = (in[0] << 8) | in[1]; + + while (curr < end) { + /* in byte */ + + result = 0; + for (i = 0; i < 8; i++) { + /* decode */ + + while ((q & UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN)) { + q &= ~UPPER_BIT_RUBIN; + q <<= 1; + p <<= 1; + rec_q &= ~UPPER_BIT_RUBIN; + rec_q <<= 1; + rec_q |= (temp >> (bit++ ^ 7)) & 1; + if (bit > 31) { + u32 *p = (u32 *)in; + bit = 0; + temp = *(++p); + in = (unsigned char *)p; + } + } + i0 = (bits[i] * p) >> 8; + + if (i0 <= 0) i0 = 1; + /* if it fails, it fails, we have our crc + if (i0 >= p) i0 = p - 1; */ + + result >>= 1; + if (rec_q < q + i0) { + /* result |= 0x00; */ + p = i0; + } else { + result |= 0x80; + p -= i0; + q += i0; + } + } + *(curr++) = result; + } +} + +void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, + unsigned long sourcelen, unsigned long dstlen) +{ + unsigned char bits[8]; + int c; + + for (c=0; c<8; c++) + bits[c] = (256 - data_in[c]); + + rubin_do_decompress(bits, data_in+8, cpage_out, dstlen); +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c new file mode 100755 index 0000000..1b35585 --- /dev/null +++ b/fs/jffs2/compr_zlib.c @@ -0,0 +1,52 @@ +/* + * 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. + * + * $Id: compr_zlib.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $ + * + */ + +#include <common.h> +#include <config.h> +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/jffs2.h> +#include <jffs2/mini_inflate.h> + +long zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, + __u32 srclen, __u32 destlen) +{ + return (decompress_block(cpage_out, data_in + 2, ldr_memcpy)); + +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/fs/jffs2/jffs2_1pass.c b/fs/jffs2/jffs2_1pass.c new file mode 100755 index 0000000..c6c0c2a --- /dev/null +++ b/fs/jffs2/jffs2_1pass.c @@ -0,0 +1,1370 @@ +/* +------------------------------------------------------------------------- + * Filename: jffs2.c + * Version: $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $ + * Copyright: Copyright (C) 2001, Russ Dill + * Author: Russ Dill <Russ.Dill@asu.edu> + * Description: Module to load kernel from jffs2 + *-----------------------------------------------------------------------*/ +/* + * some portions of this code are taken from jffs2, and as such, the + * following copyright notice is included. + * + * 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. + * + * $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $ + * + */ + +/* Ok, so anyone who knows the jffs2 code will probably want to get a papar + * bag to throw up into before reading this code. I looked through the jffs2 + * code, the caching scheme is very elegant. I tried to keep the version + * for a bootloader as small and simple as possible. Instead of worring about + * unneccesary data copies, node scans, etc, I just optimized for the known + * common case, a kernel, which looks like: + * (1) most pages are 4096 bytes + * (2) version numbers are somewhat sorted in acsending order + * (3) multiple compressed blocks making up one page is uncommon + * + * So I create a linked list of decending version numbers (insertions at the + * head), and then for each page, walk down the list, until a matching page + * with 4096 bytes is found, and then decompress the watching pages in + * reverse order. + * + */ + +/* + * Adapted by Nye Liu <nyet@zumanetworks.com> and + * Rex Feany <rfeany@zumanetworks.com> + * on Jan/2002 for U-Boot. + * + * Clipped out all the non-1pass functions, cleaned up warnings, + * wrappers, etc. No major changes to the code. + * Please, he really means it when he said have a paper bag + * handy. We needed it ;). + * + */ + +/* + * Bugfixing by Kai-Uwe Bloem <kai-uwe.bloem@auerswald.de>, (C) Mar/2003 + * + * - overhaul of the memory management. Removed much of the "paper-bagging" + * in that part of the code, fixed several bugs, now frees memory when + * partition is changed. + * It's still ugly :-( + * - fixed a bug in jffs2_1pass_read_inode where the file length calculation + * was incorrect. Removed a bit of the paper-bagging as well. + * - removed double crc calculation for fragment headers in jffs2_private.h + * for speedup. + * - scan_empty rewritten in a more "standard" manner (non-paperbag, that is). + * - spinning wheel now spins depending on how much memory has been scanned + * - lots of small changes all over the place to "improve" readability. + * - implemented fragment sorting to ensure that the newest data is copied + * if there are multiple copies of fragments for a certain file offset. + * + * The fragment sorting feature must be enabled by CFG_JFFS2_SORT_FRAGMENTS. + * Sorting is done while adding fragments to the lists, which is more or less a + * bubble sort. This takes a lot of time, and is most probably not an issue if + * the boot filesystem is always mounted readonly. + * + * You should define it if the boot filesystem is mounted writable, and updates + * to the boot files are done by copying files to that filesystem. + * + * + * There's a big issue left: endianess is completely ignored in this code. Duh! + * + * + * You still should have paper bags at hand :-(. The code lacks more or less + * any comment, and is still arcane and difficult to read in places. As this + * might be incompatible with any new code from the jffs2 maintainers anyway, + * it should probably be dumped and replaced by something like jffs2reader! + */ + + +#include <common.h> +#include <config.h> +#include <malloc.h> +#include <linux/stat.h> +#include <linux/time.h> + +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/jffs2.h> +#include <jffs2/jffs2_1pass.h> + +#include "jffs2_private.h" + + +#define NODE_CHUNK 1024 /* size of memory allocation chunk in b_nodes */ +#define SPIN_BLKSIZE 18 /* spin after having scanned 1<<BLKSIZE bytes */ + +/* Debugging switches */ +#undef DEBUG_DIRENTS /* print directory entry list after scan */ +#undef DEBUG_FRAGMENTS /* print fragment list after scan */ +#undef DEBUG /* enable debugging messages */ + + +#ifdef DEBUG +# define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt,args...) +#endif + +/* keeps pointer to currentlu processed partition */ +static struct part_info *current_part; + +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) +/* + * Support for jffs2 on top of NAND-flash + * + * NAND memory isn't mapped in processor's address space, + * so data should be fetched from flash before + * being processed. This is exactly what functions declared + * here do. + * + */ + +/* this one defined in cmd_nand.c */ +int read_jffs2_nand(size_t start, size_t len, + size_t * retlen, u_char * buf, int nanddev); + +#define NAND_PAGE_SIZE 512 +#define NAND_PAGE_SHIFT 9 +#define NAND_PAGE_MASK (~(NAND_PAGE_SIZE-1)) + +#ifndef NAND_CACHE_PAGES +#define NAND_CACHE_PAGES 16 +#endif +#define NAND_CACHE_SIZE (NAND_CACHE_PAGES*NAND_PAGE_SIZE) + +static u8* nand_cache = NULL; +static u32 nand_cache_off = (u32)-1; + +static int read_nand_cached(u32 off, u32 size, u_char *buf) +{ + struct mtdids *id = current_part->dev->id; + u32 bytes_read = 0; + size_t retlen; + int cpy_bytes; + + while (bytes_read < size) { + if ((off + bytes_read < nand_cache_off) || + (off + bytes_read >= nand_cache_off+NAND_CACHE_SIZE)) { + nand_cache_off = (off + bytes_read) & NAND_PAGE_MASK; + if (!nand_cache) { + /* This memory never gets freed but 'cause + it's a bootloader, nobody cares */ + nand_cache = malloc(NAND_CACHE_SIZE); + if (!nand_cache) { + printf("read_nand_cached: can't alloc cache size %d bytes\n", + NAND_CACHE_SIZE); + return -1; + } + } + if (read_jffs2_nand(nand_cache_off, NAND_CACHE_SIZE, + &retlen, nand_cache, id->num) < 0 || + retlen != NAND_CACHE_SIZE) { + printf("read_nand_cached: error reading nand off %#x size %d bytes\n", + nand_cache_off, NAND_CACHE_SIZE); + return -1; + } + } + cpy_bytes = nand_cache_off + NAND_CACHE_SIZE - (off + bytes_read); + if (cpy_bytes > size - bytes_read) + cpy_bytes = size - bytes_read; + memcpy(buf + bytes_read, + nand_cache + off + bytes_read - nand_cache_off, + cpy_bytes); + bytes_read += cpy_bytes; + } + return bytes_read; +} + +static void *get_fl_mem_nand(u32 off, u32 size, void *ext_buf) +{ + u_char *buf = ext_buf ? (u_char*)ext_buf : (u_char*)malloc(size); + + if (NULL == buf) { + printf("get_fl_mem_nand: can't alloc %d bytes\n", size); + return NULL; + } + if (read_nand_cached(off, size, buf) < 0) { + if (!ext_buf) + free(buf); + return NULL; + } + + return buf; +} + +static void *get_node_mem_nand(u32 off) +{ + struct jffs2_unknown_node node; + void *ret = NULL; + + if (NULL == get_fl_mem_nand(off, sizeof(node), &node)) + return NULL; + + if (!(ret = get_fl_mem_nand(off, node.magic == + JFFS2_MAGIC_BITMASK ? node.totlen : sizeof(node), + NULL))) { + printf("off = %#x magic %#x type %#x node.totlen = %d\n", + off, node.magic, node.nodetype, node.totlen); + } + return ret; +} + +static void put_fl_mem_nand(void *buf) +{ + free(buf); +} +#endif /* #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) */ + + +#if (CONFIG_COMMANDS & CFG_CMD_FLASH) +/* + * Support for jffs2 on top of NOR-flash + * + * NOR flash memory is mapped in processor's address space, + * just return address. + */ +static inline void *get_fl_mem_nor(u32 off) +{ + u32 addr = off; + struct mtdids *id = current_part->dev->id; + + extern flash_info_t flash_info[]; + flash_info_t *flash = &flash_info[id->num]; + + addr += flash->start[0]; + return (void*)addr; +} + +static inline void *get_node_mem_nor(u32 off) +{ + return (void*)get_fl_mem_nor(off); +} +#endif /* #if (CONFIG_COMMANDS & CFG_CMD_FLASH) */ + + +/* + * Generic jffs2 raw memory and node read routines. + * + */ +static inline void *get_fl_mem(u32 off, u32 size, void *ext_buf) +{ + struct mtdids *id = current_part->dev->id; + +#if (CONFIG_COMMANDS & CFG_CMD_FLASH) + if (id->type == MTD_DEV_TYPE_NOR) + return get_fl_mem_nor(off); +#endif + +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) + if (id->type == MTD_DEV_TYPE_NAND) + return get_fl_mem_nand(off, size, ext_buf); +#endif + + printf("get_fl_mem: unknown device type, using raw offset!\n"); + return (void*)off; +} + +static inline void *get_node_mem(u32 off) +{ + struct mtdids *id = current_part->dev->id; + +#if (CONFIG_COMMANDS & CFG_CMD_FLASH) + if (id->type == MTD_DEV_TYPE_NOR) + return get_node_mem_nor(off); +#endif + +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) + if (id->type == MTD_DEV_TYPE_NAND) + return get_node_mem_nand(off); +#endif + + printf("get_node_mem: unknown device type, using raw offset!\n"); + return (void*)off; +} + +static inline void put_fl_mem(void *buf) +{ +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) + struct mtdids *id = current_part->dev->id; + + if (id->type == MTD_DEV_TYPE_NAND) + return put_fl_mem_nand(buf); +#endif +} + +/* Compression names */ +static char *compr_names[] = { + "NONE", + "ZERO", + "RTIME", + "RUBINMIPS", + "COPY", + "DYNRUBIN", + "ZLIB", +#if defined(CONFIG_JFFS2_LZO_LZARI) + "LZO", + "LZARI", +#endif +}; + +/* Spinning wheel */ +static char spinner[] = { '|', '/', '-', '\\' }; + +/* Memory management */ +struct mem_block { + u32 index; + struct mem_block *next; + struct b_node nodes[NODE_CHUNK]; +}; + + +static void +free_nodes(struct b_list *list) +{ + while (list->listMemBase != NULL) { + struct mem_block *next = list->listMemBase->next; + free( list->listMemBase ); + list->listMemBase = next; + } +} + +static struct b_node * +add_node(struct b_list *list) +{ + u32 index = 0; + struct mem_block *memBase; + struct b_node *b; + + memBase = list->listMemBase; + if (memBase != NULL) + index = memBase->index; +#if 0 + putLabeledWord("add_node: index = ", index); + putLabeledWord("add_node: memBase = ", list->listMemBase); +#endif + + if (memBase == NULL || index >= NODE_CHUNK) { + /* we need more space before we continue */ + memBase = mmalloc(sizeof(struct mem_block)); + if (memBase == NULL) { + putstr("add_node: malloc failed\n"); + return NULL; + } + memBase->next = list->listMemBase; + index = 0; +#if 0 + putLabeledWord("add_node: alloced a new membase at ", *memBase); +#endif + + } + /* now we have room to add it. */ + b = &memBase->nodes[index]; + index ++; + + memBase->index = index; + list->listMemBase = memBase; + list->listCount++; + return b; +} + +static struct b_node * +insert_node(struct b_list *list, u32 offset) +{ + struct b_node *new; +#ifdef CFG_JFFS2_SORT_FRAGMENTS + struct b_node *b, *prev; +#endif + + if (!(new = add_node(list))) { + putstr("add_node failed!\r\n"); + return NULL; + } + new->offset = offset; + +#ifdef CFG_JFFS2_SORT_FRAGMENTS + if (list->listTail != NULL && list->listCompare(new, list->listTail)) + prev = list->listTail; + else if (list->listLast != NULL && list->listCompare(new, list->listLast)) + prev = list->listLast; + else + prev = NULL; + + for (b = (prev ? prev->next : list->listHead); + b != NULL && list->listCompare(new, b); + prev = b, b = b->next) { + list->listLoops++; + } + if (b != NULL) + list->listLast = prev; + + if (b != NULL) { + new->next = b; + if (prev != NULL) + prev->next = new; + else + list->listHead = new; + } else +#endif + { + new->next = (struct b_node *) NULL; + if (list->listTail != NULL) { + list->listTail->next = new; + list->listTail = new; + } else { + list->listTail = list->listHead = new; + } + } + + return new; +} + +#ifdef CFG_JFFS2_SORT_FRAGMENTS +/* Sort data entries with the latest version last, so that if there + * is overlapping data the latest version will be used. + */ +static int compare_inodes(struct b_node *new, struct b_node *old) +{ + struct jffs2_raw_inode ojNew; + struct jffs2_raw_inode ojOld; + struct jffs2_raw_inode *jNew = + (struct jffs2_raw_inode *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); + struct jffs2_raw_inode *jOld = + (struct jffs2_raw_inode *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); + + return jNew->version > jOld->version; +} + +/* Sort directory entries so all entries in the same directory + * with the same name are grouped together, with the latest version + * last. This makes it easy to eliminate all but the latest version + * by marking the previous version dead by setting the inode to 0. + */ +static int compare_dirents(struct b_node *new, struct b_node *old) +{ + struct jffs2_raw_dirent ojNew; + struct jffs2_raw_dirent ojOld; + struct jffs2_raw_dirent *jNew = + (struct jffs2_raw_dirent *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); + struct jffs2_raw_dirent *jOld = + (struct jffs2_raw_dirent *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); + int cmp; + + /* ascending sort by pino */ + if (jNew->pino != jOld->pino) + return jNew->pino > jOld->pino; + + /* pino is the same, so use ascending sort by nsize, so + * we don't do strncmp unless we really must. + */ + if (jNew->nsize != jOld->nsize) + return jNew->nsize > jOld->nsize; + + /* length is also the same, so use ascending sort by name + */ + cmp = strncmp((char *)jNew->name, (char *)jOld->name, jNew->nsize); + if (cmp != 0) + return cmp > 0; + + /* we have duplicate names in this directory, so use ascending + * sort by version + */ + if (jNew->version > jOld->version) { + /* since jNew is newer, we know jOld is not valid, so + * mark it with inode 0 and it will not be used + */ + jOld->ino = 0; + return 1; + } + + return 0; +} +#endif + +static u32 +jffs2_scan_empty(u32 start_offset, struct part_info *part) +{ + char *max = (char *)(part->offset + part->size - sizeof(struct jffs2_raw_inode)); + char *offset = (char *)(part->offset + start_offset); + u32 off; + + while (offset < max && + *(u32*)get_fl_mem((u32)offset, sizeof(u32), &off) == 0xFFFFFFFF) { + offset += sizeof(u32); + /* return if spinning is due */ + if (((u32)offset & ((1 << SPIN_BLKSIZE)-1)) == 0) break; + } + + return (u32)offset - part->offset; +} + +void +jffs2_free_cache(struct part_info *part) +{ + struct b_lists *pL; + + if (part->jffs2_priv != NULL) { + pL = (struct b_lists *)part->jffs2_priv; + free_nodes(&pL->frag); + free_nodes(&pL->dir); + free(pL); + } +} + +static u32 +jffs_init_1pass_list(struct part_info *part) +{ + struct b_lists *pL; + + jffs2_free_cache(part); + + if (NULL != (part->jffs2_priv = malloc(sizeof(struct b_lists)))) { + pL = (struct b_lists *)part->jffs2_priv; + + memset(pL, 0, sizeof(*pL)); +#ifdef CFG_JFFS2_SORT_FRAGMENTS + pL->dir.listCompare = compare_dirents; + pL->frag.listCompare = compare_inodes; +#endif + } + return 0; +} + +/* find the inode from the slashless name given a parent */ +static long +jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) +{ + struct b_node *b; + struct jffs2_raw_inode *jNode; + u32 totalSize = 0; + u32 latestVersion = 0; + uchar *lDest; + uchar *src; + long ret; + int i; + u32 counter = 0; +#ifdef CFG_JFFS2_SORT_FRAGMENTS + /* Find file size before loading any data, so fragments that + * start past the end of file can be ignored. A fragment + * that is partially in the file is loaded, so extra data may + * be loaded up to the next 4K boundary above the file size. + * This shouldn't cause trouble when loading kernel images, so + * we will live with it. + */ + for (b = pL->frag.listHead; b != NULL; b = b->next) { + jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset, + sizeof(struct jffs2_raw_inode), NULL); + if ((inode == jNode->ino)) { + /* get actual file length from the newest node */ + if (jNode->version >= latestVersion) { + totalSize = jNode->isize; + latestVersion = jNode->version; + } + } + put_fl_mem(jNode); + } +#endif + + for (b = pL->frag.listHead; b != NULL; b = b->next) { + jNode = (struct jffs2_raw_inode *) get_node_mem(b->offset); + if ((inode == jNode->ino)) { +#if 0 + putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen); + putLabeledWord("read_inode: inode = ", jNode->ino); + putLabeledWord("read_inode: version = ", jNode->version); + putLabeledWord("read_inode: isize = ", jNode->isize); + putLabeledWord("read_inode: offset = ", jNode->offset); + putLabeledWord("read_inode: csize = ", jNode->csize); + putLabeledWord("read_inode: dsize = ", jNode->dsize); + putLabeledWord("read_inode: compr = ", jNode->compr); + putLabeledWord("read_inode: usercompr = ", jNode->usercompr); + putLabeledWord("read_inode: flags = ", jNode->flags); +#endif + +#ifndef CFG_JFFS2_SORT_FRAGMENTS + /* get actual file length from the newest node */ + if (jNode->version >= latestVersion) { + totalSize = jNode->isize; + latestVersion = jNode->version; + } +#endif + + if(dest) { + src = ((uchar *) jNode) + sizeof(struct jffs2_raw_inode); + /* ignore data behind latest known EOF */ + if (jNode->offset > totalSize) { + put_fl_mem(jNode); + continue; + } + + lDest = (uchar *) (dest + jNode->offset); +#if 0 + putLabeledWord("read_inode: src = ", src); + putLabeledWord("read_inode: dest = ", lDest); +#endif + switch (jNode->compr) { + case JFFS2_COMPR_NONE: + ret = (unsigned long) ldr_memcpy(lDest, src, jNode->dsize); + break; + case JFFS2_COMPR_ZERO: + ret = 0; + for (i = 0; i < jNode->dsize; i++) + *(lDest++) = 0; + break; + case JFFS2_COMPR_RTIME: + ret = 0; + rtime_decompress(src, lDest, jNode->csize, jNode->dsize); + break; + case JFFS2_COMPR_DYNRUBIN: + /* this is slow but it works */ + ret = 0; + dynrubin_decompress(src, lDest, jNode->csize, jNode->dsize); + break; + case JFFS2_COMPR_ZLIB: + ret = zlib_decompress(src, lDest, jNode->csize, jNode->dsize); + break; +#if defined(CONFIG_JFFS2_LZO_LZARI) + case JFFS2_COMPR_LZO: + ret = lzo_decompress(src, lDest, jNode->csize, jNode->dsize); + break; + case JFFS2_COMPR_LZARI: + ret = lzari_decompress(src, lDest, jNode->csize, jNode->dsize); + break; +#endif + default: + /* unknown */ + putLabeledWord("UNKOWN COMPRESSION METHOD = ", jNode->compr); + put_fl_mem(jNode); + return -1; + break; + } + } + +#if 0 + putLabeledWord("read_inode: totalSize = ", totalSize); + putLabeledWord("read_inode: compr ret = ", ret); +#endif + } + counter++; + put_fl_mem(jNode); + } + +#if 0 + putLabeledWord("read_inode: returning = ", totalSize); +#endif + return totalSize; +} + +/* find the inode from the slashless name given a parent */ +static u32 +jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino) +{ + struct b_node *b; + struct jffs2_raw_dirent *jDir; + int len; + u32 counter; + u32 version = 0; + u32 inode = 0; + + /* name is assumed slash free */ + len = strlen(name); + + counter = 0; + /* we need to search all and return the inode with the highest version */ + for(b = pL->dir.listHead; b; b = b->next, counter++) { + jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset); + if ((pino == jDir->pino) && (len == jDir->nsize) && + (jDir->ino) && /* 0 for unlink */ + (!strncmp((char *)jDir->name, name, len))) { /* a match */ + if (jDir->version < version) { + put_fl_mem(jDir); + continue; + } + + if (jDir->version == version && inode != 0) { + /* I'm pretty sure this isn't legal */ + putstr(" ** ERROR ** "); + putnstr(jDir->name, jDir->nsize); + putLabeledWord(" has dup version =", version); + } + inode = jDir->ino; + version = jDir->version; + } +#if 0 + putstr("\r\nfind_inode:p&l ->"); + putnstr(jDir->name, jDir->nsize); + putstr("\r\n"); + putLabeledWord("pino = ", jDir->pino); + putLabeledWord("nsize = ", jDir->nsize); + putLabeledWord("b = ", (u32) b); + putLabeledWord("counter = ", counter); +#endif + put_fl_mem(jDir); + } + return inode; +} + +char *mkmodestr(unsigned long mode, char *str) +{ + static const char *l = "xwr"; + int mask = 1, i; + char c; + + switch (mode & S_IFMT) { + case S_IFDIR: str[0] = 'd'; break; + case S_IFBLK: str[0] = 'b'; break; + case S_IFCHR: str[0] = 'c'; break; + case S_IFIFO: str[0] = 'f'; break; + case S_IFLNK: str[0] = 'l'; break; + case S_IFSOCK: str[0] = 's'; break; + case S_IFREG: str[0] = '-'; break; + default: str[0] = '?'; + } + + for(i = 0; i < 9; i++) { + c = l[i%3]; + str[9-i] = (mode & mask)?c:'-'; + mask = mask<<1; + } + + if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S'; + if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S'; + if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T'; + str[10] = '\0'; + return str; +} + +static inline void dump_stat(struct stat *st, const char *name) +{ + char str[20]; + char s[64], *p; + + if (st->st_mtime == (time_t)(-1)) /* some ctimes really hate -1 */ + st->st_mtime = 1; + + ctime_r((time_t *)&st->st_mtime, s/*,64*/); /* newlib ctime doesn't have buflen */ + + if ((p = strchr(s,'\n')) != NULL) *p = '\0'; + if ((p = strchr(s,'\r')) != NULL) *p = '\0'; + +/* + printf("%6lo %s %8ld %s %s\n", st->st_mode, mkmodestr(st->st_mode, str), + st->st_size, s, name); +*/ + + printf(" %s %8ld %s %s", mkmodestr(st->st_mode,str), st->st_size, s, name); +} + +static inline u32 dump_inode(struct b_lists * pL, struct jffs2_raw_dirent *d, struct jffs2_raw_inode *i) +{ + char fname[256]; + struct stat st; + + if(!d || !i) return -1; + + strncpy(fname, (char *)d->name, d->nsize); + fname[d->nsize] = '\0'; + + memset(&st,0,sizeof(st)); + + st.st_mtime = i->mtime; + st.st_mode = i->mode; + st.st_ino = i->ino; + + /* neither dsize nor isize help us.. do it the long way */ + st.st_size = jffs2_1pass_read_inode(pL, i->ino, NULL); + + dump_stat(&st, fname); + + if (d->type == DT_LNK) { + unsigned char *src = (unsigned char *) (&i[1]); + putstr(" -> "); + putnstr(src, (int)i->dsize); + } + + putstr("\r\n"); + + return 0; +} + +/* list inodes with the given pino */ +static u32 +jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino) +{ + struct b_node *b; + struct jffs2_raw_dirent *jDir; + + for (b = pL->dir.listHead; b; b = b->next) { + jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset); + if ((pino == jDir->pino) && (jDir->ino)) { /* ino=0 -> unlink */ + u32 i_version = 0; + struct jffs2_raw_inode ojNode; + struct jffs2_raw_inode *jNode, *i = NULL; + struct b_node *b2 = pL->frag.listHead; + + while (b2) { + jNode = (struct jffs2_raw_inode *) + get_fl_mem(b2->offset, sizeof(ojNode), &ojNode); + if (jNode->ino == jDir->ino && jNode->version >= i_version) { + if (i) + put_fl_mem(i); + + if (jDir->type == DT_LNK) + i = get_node_mem(b2->offset); + else + i = get_fl_mem(b2->offset, sizeof(*i), NULL); + } + b2 = b2->next; + } + + dump_inode(pL, jDir, i); + put_fl_mem(i); + } + put_fl_mem(jDir); + } + return pino; +} + +static u32 +jffs2_1pass_search_inode(struct b_lists * pL, const char *fname, u32 pino) +{ + int i; + char tmp[256]; + char working_tmp[256]; + char *c; + + /* discard any leading slash */ + i = 0; + while (fname[i] == '/') + i++; + strcpy(tmp, &fname[i]); + + while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ + { + strncpy(working_tmp, tmp, c - tmp); + working_tmp[c - tmp] = '\0'; +#if 0 + putstr("search_inode: tmp = "); + putstr(tmp); + putstr("\r\n"); + putstr("search_inode: wtmp = "); + putstr(working_tmp); + putstr("\r\n"); + putstr("search_inode: c = "); + putstr(c); + putstr("\r\n"); +#endif + for (i = 0; i < strlen(c) - 1; i++) + tmp[i] = c[i + 1]; + tmp[i] = '\0'; +#if 0 + putstr("search_inode: post tmp = "); + putstr(tmp); + putstr("\r\n"); +#endif + + if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino))) { + putstr("find_inode failed for name="); + putstr(working_tmp); + putstr("\r\n"); + return 0; + } + } + /* this is for the bare filename, directories have already been mapped */ + if (!(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + return pino; + +} + +static u32 +jffs2_1pass_resolve_inode(struct b_lists * pL, u32 ino) +{ + struct b_node *b; + struct b_node *b2; + struct jffs2_raw_dirent *jDir; + struct jffs2_raw_inode *jNode; + u8 jDirFoundType = 0; + u32 jDirFoundIno = 0; + u32 jDirFoundPino = 0; + char tmp[256]; + u32 version = 0; + u32 pino; + unsigned char *src; + + /* we need to search all and return the inode with the highest version */ + for(b = pL->dir.listHead; b; b = b->next) { + jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset); + if (ino == jDir->ino) { + if (jDir->version < version) { + put_fl_mem(jDir); + continue; + } + + if (jDir->version == version && jDirFoundType) { + /* I'm pretty sure this isn't legal */ + putstr(" ** ERROR ** "); + putnstr(jDir->name, jDir->nsize); + putLabeledWord(" has dup version (resolve) = ", + version); + } + + jDirFoundType = jDir->type; + jDirFoundIno = jDir->ino; + jDirFoundPino = jDir->pino; + version = jDir->version; + } + put_fl_mem(jDir); + } + /* now we found the right entry again. (shoulda returned inode*) */ + if (jDirFoundType != DT_LNK) + return jDirFoundIno; + + /* it's a soft link so we follow it again. */ + b2 = pL->frag.listHead; + while (b2) { + jNode = (struct jffs2_raw_inode *) get_node_mem(b2->offset); + if (jNode->ino == jDirFoundIno) { + src = (unsigned char *)jNode + sizeof(struct jffs2_raw_inode); + +#if 0 + putLabeledWord("\t\t dsize = ", jNode->dsize); + putstr("\t\t target = "); + putnstr(src, jNode->dsize); + putstr("\r\n"); +#endif + strncpy(tmp, (char *)src, jNode->dsize); + tmp[jNode->dsize] = '\0'; + put_fl_mem(jNode); + break; + } + b2 = b2->next; + put_fl_mem(jNode); + } + /* ok so the name of the new file to find is in tmp */ + /* if it starts with a slash it is root based else shared dirs */ + if (tmp[0] == '/') + pino = 1; + else + pino = jDirFoundPino; + + return jffs2_1pass_search_inode(pL, tmp, pino); +} + +static u32 +jffs2_1pass_search_list_inodes(struct b_lists * pL, const char *fname, u32 pino) +{ + int i; + char tmp[256]; + char working_tmp[256]; + char *c; + + /* discard any leading slash */ + i = 0; + while (fname[i] == '/') + i++; + strcpy(tmp, &fname[i]); + working_tmp[0] = '\0'; + while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ + { + strncpy(working_tmp, tmp, c - tmp); + working_tmp[c - tmp] = '\0'; + for (i = 0; i < strlen(c) - 1; i++) + tmp[i] = c[i + 1]; + tmp[i] = '\0'; + /* only a failure if we arent looking at top level */ + if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino)) && + (working_tmp[0])) { + putstr("find_inode failed for name="); + putstr(working_tmp); + putstr("\r\n"); + return 0; + } + } + + if (tmp[0] && !(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + /* this is for the bare filename, directories have already been mapped */ + if (!(pino = jffs2_1pass_list_inodes(pL, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + return pino; + +} + +unsigned char +jffs2_1pass_rescan_needed(struct part_info *part) +{ + struct b_node *b; + struct jffs2_unknown_node onode; + struct jffs2_unknown_node *node; + struct b_lists *pL = (struct b_lists *)part->jffs2_priv; + + if (part->jffs2_priv == 0){ + DEBUGF ("rescan: First time in use\n"); + return 1; + } + + /* if we have no list, we need to rescan */ + if (pL->frag.listCount == 0) { + DEBUGF ("rescan: fraglist zero\n"); + return 1; + } + + /* but suppose someone reflashed a partition at the same offset... */ + b = pL->dir.listHead; + while (b) { + node = (struct jffs2_unknown_node *) get_fl_mem(b->offset, + sizeof(onode), &onode); + if (node->nodetype != JFFS2_NODETYPE_DIRENT) { + DEBUGF ("rescan: fs changed beneath me? (%lx)\n", + (unsigned long) b->offset); + return 1; + } + b = b->next; + } + return 0; +} + +#ifdef DEBUG_FRAGMENTS +static void +dump_fragments(struct b_lists *pL) +{ + struct b_node *b; + struct jffs2_raw_inode ojNode; + struct jffs2_raw_inode *jNode; + + putstr("\r\n\r\n******The fragment Entries******\r\n"); + b = pL->frag.listHead; + while (b) { + jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset, + sizeof(ojNode), &ojNode); + putLabeledWord("\r\n\tbuild_list: FLASH_OFFSET = ", b->offset); + putLabeledWord("\tbuild_list: totlen = ", jNode->totlen); + putLabeledWord("\tbuild_list: inode = ", jNode->ino); + putLabeledWord("\tbuild_list: version = ", jNode->version); + putLabeledWord("\tbuild_list: isize = ", jNode->isize); + putLabeledWord("\tbuild_list: atime = ", jNode->atime); + putLabeledWord("\tbuild_list: offset = ", jNode->offset); + putLabeledWord("\tbuild_list: csize = ", jNode->csize); + putLabeledWord("\tbuild_list: dsize = ", jNode->dsize); + putLabeledWord("\tbuild_list: compr = ", jNode->compr); + putLabeledWord("\tbuild_list: usercompr = ", jNode->usercompr); + putLabeledWord("\tbuild_list: flags = ", jNode->flags); + putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */ + b = b->next; + } +} +#endif + +#ifdef DEBUG_DIRENTS +static void +dump_dirents(struct b_lists *pL) +{ + struct b_node *b; + struct jffs2_raw_dirent *jDir; + + putstr("\r\n\r\n******The directory Entries******\r\n"); + b = pL->dir.listHead; + while (b) { + jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset); + putstr("\r\n"); + putnstr(jDir->name, jDir->nsize); + putLabeledWord("\r\n\tbuild_list: magic = ", jDir->magic); + putLabeledWord("\tbuild_list: nodetype = ", jDir->nodetype); + putLabeledWord("\tbuild_list: hdr_crc = ", jDir->hdr_crc); + putLabeledWord("\tbuild_list: pino = ", jDir->pino); + putLabeledWord("\tbuild_list: version = ", jDir->version); + putLabeledWord("\tbuild_list: ino = ", jDir->ino); + putLabeledWord("\tbuild_list: mctime = ", jDir->mctime); + putLabeledWord("\tbuild_list: nsize = ", jDir->nsize); + putLabeledWord("\tbuild_list: type = ", jDir->type); + putLabeledWord("\tbuild_list: node_crc = ", jDir->node_crc); + putLabeledWord("\tbuild_list: name_crc = ", jDir->name_crc); + putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */ + b = b->next; + put_fl_mem(jDir); + } +} +#endif + +static u32 +jffs2_1pass_build_lists(struct part_info * part) +{ + struct b_lists *pL; + struct jffs2_unknown_node *node; + u32 offset, oldoffset = 0; + u32 max = part->size - sizeof(struct jffs2_raw_inode); + u32 counter = 0; + u32 counter4 = 0; + u32 counterF = 0; + u32 counterN = 0; + + /* turn off the lcd. Refreshing the lcd adds 50% overhead to the */ + /* jffs2 list building enterprise nope. in newer versions the overhead is */ + /* only about 5 %. not enough to inconvenience people for. */ + /* lcd_off(); */ + + /* if we are building a list we need to refresh the cache. */ + jffs_init_1pass_list(part); + pL = (struct b_lists *)part->jffs2_priv; + offset = 0; + puts ("Scanning JFFS2 FS: "); + + /* start at the beginning of the partition */ + while (offset < max) { + if ((oldoffset >> SPIN_BLKSIZE) != (offset >> SPIN_BLKSIZE)) { + printf("\b\b%c ", spinner[counter++ % sizeof(spinner)]); + oldoffset = offset; + } + + node = (struct jffs2_unknown_node *) get_node_mem((u32)part->offset + offset); + if (node->magic == JFFS2_MAGIC_BITMASK && hdr_crc(node)) { + /* if its a fragment add it */ + if (node->nodetype == JFFS2_NODETYPE_INODE && + inode_crc((struct jffs2_raw_inode *) node)) { + if (insert_node(&pL->frag, (u32) part->offset + + offset) == NULL) { + put_fl_mem(node); + return 0; + } + } else if (node->nodetype == JFFS2_NODETYPE_DIRENT && + dirent_crc((struct jffs2_raw_dirent *) node) && + dirent_name_crc((struct jffs2_raw_dirent *) node)) { + if (! (counterN%100)) + puts ("\b\b. "); + if (insert_node(&pL->dir, (u32) part->offset + + offset) == NULL) { + put_fl_mem(node); + return 0; + } + counterN++; + } else if (node->nodetype == JFFS2_NODETYPE_CLEANMARKER) { + if (node->totlen != sizeof(struct jffs2_unknown_node)) + printf("OOPS Cleanmarker has bad size " + "%d != %d\n", node->totlen, + sizeof(struct jffs2_unknown_node)); + } else if (node->nodetype == JFFS2_NODETYPE_PADDING) { + if (node->totlen < sizeof(struct jffs2_unknown_node)) + printf("OOPS Padding has bad size " + "%d < %d\n", node->totlen, + sizeof(struct jffs2_unknown_node)); + } else { + printf("Unknown node type: %x len %d " + "offset 0x%x\n", node->nodetype, + node->totlen, offset); + } + offset += ((node->totlen + 3) & ~3); + counterF++; + } else if (node->magic == JFFS2_EMPTY_BITMASK && + node->nodetype == JFFS2_EMPTY_BITMASK) { + offset = jffs2_scan_empty(offset, part); + } else { /* if we know nothing, we just step and look. */ + offset += 4; + counter4++; + } +/* printf("unknown node magic %4.4x %4.4x @ %lx\n", node->magic, node->nodetype, (unsigned long)node); */ + put_fl_mem(node); + } + + putstr("\b\b done.\r\n"); /* close off the dots */ + /* turn the lcd back on. */ + /* splash(); */ + +#if 0 + putLabeledWord("dir entries = ", pL->dir.listCount); + putLabeledWord("frag entries = ", pL->frag.listCount); + putLabeledWord("+4 increments = ", counter4); + putLabeledWord("+file_offset increments = ", counterF); + +#endif + +#ifdef DEBUG_DIRENTS + dump_dirents(pL); +#endif + +#ifdef DEBUG_FRAGMENTS + dump_fragments(pL); +#endif + + /* give visual feedback that we are done scanning the flash */ + led_blink(0x0, 0x0, 0x1, 0x1); /* off, forever, on 100ms, off 100ms */ + return 1; +} + + +static u32 +jffs2_1pass_fill_info(struct b_lists * pL, struct b_jffs2_info * piL) +{ + struct b_node *b; + struct jffs2_raw_inode ojNode; + struct jffs2_raw_inode *jNode; + int i; + + for (i = 0; i < JFFS2_NUM_COMPR; i++) { + piL->compr_info[i].num_frags = 0; + piL->compr_info[i].compr_sum = 0; + piL->compr_info[i].decompr_sum = 0; + } + + b = pL->frag.listHead; + while (b) { + jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset, + sizeof(ojNode), &ojNode); + if (jNode->compr < JFFS2_NUM_COMPR) { + piL->compr_info[jNode->compr].num_frags++; + piL->compr_info[jNode->compr].compr_sum += jNode->csize; + piL->compr_info[jNode->compr].decompr_sum += jNode->dsize; + } + b = b->next; + } + return 0; +} + + +static struct b_lists * +jffs2_get_list(struct part_info * part, const char *who) +{ + /* copy requested part_info struct pointer to global location */ + current_part = part; + + if (jffs2_1pass_rescan_needed(part)) { + if (!jffs2_1pass_build_lists(part)) { + printf("%s: Failed to scan JFFSv2 file structure\n", who); + return NULL; + } + } + return (struct b_lists *)part->jffs2_priv; +} + + +/* Print directory / file contents */ +u32 +jffs2_1pass_ls(struct part_info * part, const char *fname) +{ + struct b_lists *pl; + long ret = 1; + u32 inode; + + if (! (pl = jffs2_get_list(part, "ls"))) + return 0; + + if (! (inode = jffs2_1pass_search_list_inodes(pl, fname, 1))) { + putstr("ls: Failed to scan jffs2 file structure\r\n"); + return 0; + } + + +#if 0 + putLabeledWord("found file at inode = ", inode); + putLabeledWord("read_inode returns = ", ret); +#endif + + return ret; +} + + +/* Load a file from flash into memory. fname can be a full path */ +u32 +jffs2_1pass_load(char *dest, struct part_info * part, const char *fname) +{ + + struct b_lists *pl; + long ret = 1; + u32 inode; + + if (! (pl = jffs2_get_list(part, "load"))) + return 0; + + if (! (inode = jffs2_1pass_search_inode(pl, fname, 1))) { + putstr("load: Failed to find inode\r\n"); + return 0; + } + + /* Resolve symlinks */ + if (! (inode = jffs2_1pass_resolve_inode(pl, inode))) { + putstr("load: Failed to resolve inode structure\r\n"); + return 0; + } + + if ((ret = jffs2_1pass_read_inode(pl, inode, dest)) < 0) { + putstr("load: Failed to read inode\r\n"); + return 0; + } + + DEBUGF ("load: loaded '%s' to 0x%lx (%ld bytes)\n", fname, + (unsigned long) dest, ret); + return ret; +} + +/* Return information about the fs on this partition */ +u32 +jffs2_1pass_info(struct part_info * part) +{ + struct b_jffs2_info info; + struct b_lists *pl; + int i; + + if (! (pl = jffs2_get_list(part, "info"))) + return 0; + + jffs2_1pass_fill_info(pl, &info); + for (i = 0; i < JFFS2_NUM_COMPR; i++) { + printf ("Compression: %s\n" + "\tfrag count: %d\n" + "\tcompressed sum: %d\n" + "\tuncompressed sum: %d\n", + compr_names[i], + info.compr_info[i].num_frags, + info.compr_info[i].compr_sum, + info.compr_info[i].decompr_sum); + } + return 1; +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/fs/jffs2/jffs2_private.h b/fs/jffs2/jffs2_private.h new file mode 100755 index 0000000..65ca6eb --- /dev/null +++ b/fs/jffs2/jffs2_private.h @@ -0,0 +1,88 @@ +#ifndef jffs2_private_h +#define jffs2_private_h + +#include <jffs2/jffs2.h> + + +struct b_node { + u32 offset; + struct b_node *next; +}; + +struct b_list { + struct b_node *listTail; + struct b_node *listHead; +#ifdef CFG_JFFS2_SORT_FRAGMENTS + struct b_node *listLast; + int (*listCompare)(struct b_node *new, struct b_node *node); + u32 listLoops; +#endif + u32 listCount; + struct mem_block *listMemBase; +}; + +struct b_lists { + struct b_list dir; + struct b_list frag; + +}; + +struct b_compr_info { + u32 num_frags; + u32 compr_sum; + u32 decompr_sum; +}; + +struct b_jffs2_info { + struct b_compr_info compr_info[JFFS2_NUM_COMPR]; +}; + +static inline int +hdr_crc(struct jffs2_unknown_node *node) +{ +#if 1 + u32 crc = crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4); +#else + /* what's the semantics of this? why is this here? */ + u32 crc = crc32_no_comp(~0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4); + + crc ^= ~0; +#endif + if (node->hdr_crc != crc) { + return 0; + } else { + return 1; + } +} + +static inline int +dirent_crc(struct jffs2_raw_dirent *node) +{ + if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_dirent) - 8)) { + return 0; + } else { + return 1; + } +} + +static inline int +dirent_name_crc(struct jffs2_raw_dirent *node) +{ + if (node->name_crc != crc32_no_comp(0, (unsigned char *)&(node->name), node->nsize)) { + return 0; + } else { + return 1; + } +} + +static inline int +inode_crc(struct jffs2_raw_inode *node) +{ + if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_inode) - 8)) { + return 0; + } else { + return 1; + } +} + +#endif /* jffs2_private.h */ diff --git a/fs/jffs2/mini_inflate.c b/fs/jffs2/mini_inflate.c new file mode 100755 index 0000000..4f511ec --- /dev/null +++ b/fs/jffs2/mini_inflate.c @@ -0,0 +1,396 @@ +/*------------------------------------------------------------------------- + * Filename: mini_inflate.c + * Version: $Id: mini_inflate.c,v 1.3 2002/01/24 22:58:42 rfeany Exp $ + * Copyright: Copyright (C) 2001, Russ Dill + * Author: Russ Dill <Russ.Dill@asu.edu> + * Description: Mini inflate implementation (RFC 1951) + *-----------------------------------------------------------------------*/ +/* + * + * 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 + * + */ + +#include <config.h> + +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/mini_inflate.h> + +/* The order that the code lengths in section 3.2.7 are in */ +static unsigned char huffman_order[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +inline void cramfs_memset(int *s, const int c, size n) +{ + n--; + for (;n > 0; n--) s[n] = c; + s[0] = c; +} + +/* associate a stream with a block of data and reset the stream */ +static void init_stream(struct bitstream *stream, unsigned char *data, + void *(*inflate_memcpy)(void *, const void *, size)) +{ + stream->error = NO_ERROR; + stream->memcpy = inflate_memcpy; + stream->decoded = 0; + stream->data = data; + stream->bit = 0; /* The first bit of the stream is the lsb of the + * first byte */ + + /* really sorry about all this initialization, think of a better way, + * let me know and it will get cleaned up */ + stream->codes.bits = 8; + stream->codes.num_symbols = 19; + stream->codes.lengths = stream->code_lengths; + stream->codes.symbols = stream->code_symbols; + stream->codes.count = stream->code_count; + stream->codes.first = stream->code_first; + stream->codes.pos = stream->code_pos; + + stream->lengths.bits = 16; + stream->lengths.num_symbols = 288; + stream->lengths.lengths = stream->length_lengths; + stream->lengths.symbols = stream->length_symbols; + stream->lengths.count = stream->length_count; + stream->lengths.first = stream->length_first; + stream->lengths.pos = stream->length_pos; + + stream->distance.bits = 16; + stream->distance.num_symbols = 32; + stream->distance.lengths = stream->distance_lengths; + stream->distance.symbols = stream->distance_symbols; + stream->distance.count = stream->distance_count; + stream->distance.first = stream->distance_first; + stream->distance.pos = stream->distance_pos; + +} + +/* pull 'bits' bits out of the stream. The last bit pulled it returned as the + * msb. (section 3.1.1) + */ +inline unsigned long pull_bits(struct bitstream *stream, + const unsigned int bits) +{ + unsigned long ret; + int i; + + ret = 0; + for (i = 0; i < bits; i++) { + ret += ((*(stream->data) >> stream->bit) & 1) << i; + + /* if, before incrementing, we are on bit 7, + * go to the lsb of the next byte */ + if (stream->bit++ == 7) { + stream->bit = 0; + stream->data++; + } + } + return ret; +} + +inline int pull_bit(struct bitstream *stream) +{ + int ret = ((*(stream->data) >> stream->bit) & 1); + if (stream->bit++ == 7) { + stream->bit = 0; + stream->data++; + } + return ret; +} + +/* discard bits up to the next whole byte */ +static void discard_bits(struct bitstream *stream) +{ + if (stream->bit != 0) { + stream->bit = 0; + stream->data++; + } +} + +/* No decompression, the data is all literals (section 3.2.4) */ +static void decompress_none(struct bitstream *stream, unsigned char *dest) +{ + unsigned int length; + + discard_bits(stream); + length = *(stream->data++); + length += *(stream->data++) << 8; + pull_bits(stream, 16); /* throw away the inverse of the size */ + + stream->decoded += length; + stream->memcpy(dest, stream->data, length); + stream->data += length; +} + +/* Read in a symbol from the stream (section 3.2.2) */ +static int read_symbol(struct bitstream *stream, struct huffman_set *set) +{ + int bits = 0; + int code = 0; + while (!(set->count[bits] && code < set->first[bits] + + set->count[bits])) { + code = (code << 1) + pull_bit(stream); + if (++bits > set->bits) { + /* error decoding (corrupted data?) */ + stream->error = CODE_NOT_FOUND; + return -1; + } + } + return set->symbols[set->pos[bits] + code - set->first[bits]]; +} + +/* decompress a stream of data encoded with the passed length and distance + * huffman codes */ +static void decompress_huffman(struct bitstream *stream, unsigned char *dest) +{ + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + int symbol, length, dist, i; + + do { + if ((symbol = read_symbol(stream, lengths)) < 0) return; + if (symbol < 256) { + *(dest++) = symbol; /* symbol is a literal */ + stream->decoded++; + } else if (symbol > 256) { + /* Determine the length of the repitition + * (section 3.2.5) */ + if (symbol < 265) length = symbol - 254; + else if (symbol == 285) length = 258; + else { + length = pull_bits(stream, (symbol - 261) >> 2); + length += (4 << ((symbol - 261) >> 2)) + 3; + length += ((symbol - 1) % 4) << + ((symbol - 261) >> 2); + } + + /* Determine how far back to go */ + if ((symbol = read_symbol(stream, distance)) < 0) + return; + if (symbol < 4) dist = symbol + 1; + else { + dist = pull_bits(stream, (symbol - 2) >> 1); + dist += (2 << ((symbol - 2) >> 1)) + 1; + dist += (symbol % 2) << ((symbol - 2) >> 1); + } + stream->decoded += length; + for (i = 0; i < length; i++) { + *dest = dest[-dist]; + dest++; + } + } + } while (symbol != 256); /* 256 is the end of the data block */ +} + +/* Fill the lookup tables (section 3.2.2) */ +static void fill_code_tables(struct huffman_set *set) +{ + int code = 0, i, length; + + /* fill in the first code of each bit length, and the pos pointer */ + set->pos[0] = 0; + for (i = 1; i < set->bits; i++) { + code = (code + set->count[i - 1]) << 1; + set->first[i] = code; + set->pos[i] = set->pos[i - 1] + set->count[i - 1]; + } + + /* Fill in the table of symbols in order of their huffman code */ + for (i = 0; i < set->num_symbols; i++) { + if ((length = set->lengths[i])) + set->symbols[set->pos[length]++] = i; + } + + /* reset the pos pointer */ + for (i = 1; i < set->bits; i++) set->pos[i] -= set->count[i]; +} + +static void init_code_tables(struct huffman_set *set) +{ + cramfs_memset(set->lengths, 0, set->num_symbols); + cramfs_memset(set->count, 0, set->bits); + cramfs_memset(set->first, 0, set->bits); +} + +/* read in the huffman codes for dynamic decoding (section 3.2.7) */ +static void decompress_dynamic(struct bitstream *stream, unsigned char *dest) +{ + /* I tried my best to minimize the memory footprint here, while still + * keeping up performance. I really dislike the _lengths[] tables, but + * I see no way of eliminating them without a sizable performance + * impact. The first struct table keeps track of stats on each bit + * length. The _length table keeps a record of the bit length of each + * symbol. The _symbols table is for looking up symbols by the huffman + * code (the pos element points to the first place in the symbol table + * where that bit length occurs). I also hate the initization of these + * structs, if someone knows how to compact these, lemme know. */ + + struct huffman_set *codes = &(stream->codes); + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + int hlit = pull_bits(stream, 5) + 257; + int hdist = pull_bits(stream, 5) + 1; + int hclen = pull_bits(stream, 4) + 4; + int length, curr_code, symbol, i, last_code; + + last_code = 0; + + init_code_tables(codes); + init_code_tables(lengths); + init_code_tables(distance); + + /* fill in the count of each bit length' as well as the lengths + * table */ + for (i = 0; i < hclen; i++) { + length = pull_bits(stream, 3); + codes->lengths[huffman_order[i]] = length; + if (length) codes->count[length]++; + + } + fill_code_tables(codes); + + /* Do the same for the length codes, being carefull of wrap through + * to the distance table */ + curr_code = 0; + while (curr_code < hlit) { + if ((symbol = read_symbol(stream, codes)) < 0) return; + if (symbol == 0) { + curr_code++; + last_code = 0; + } else if (symbol < 16) { /* Literal length */ + lengths->lengths[curr_code] = last_code = symbol; + lengths->count[symbol]++; + curr_code++; + } else if (symbol == 16) { /* repeat the last symbol 3 - 6 + * times */ + length = 3 + pull_bits(stream, 2); + for (;length; length--, curr_code++) + if (curr_code < hlit) { + lengths->lengths[curr_code] = + last_code; + lengths->count[last_code]++; + } else { /* wrap to the distance table */ + distance->lengths[curr_code - hlit] = + last_code; + distance->count[last_code]++; + } + } else if (symbol == 17) { /* repeat a bit length 0 */ + curr_code += 3 + pull_bits(stream, 3); + last_code = 0; + } else { /* same, but more times */ + curr_code += 11 + pull_bits(stream, 7); + last_code = 0; + } + } + fill_code_tables(lengths); + + /* Fill the distance table, don't need to worry about wrapthrough + * here */ + curr_code -= hlit; + while (curr_code < hdist) { + if ((symbol = read_symbol(stream, codes)) < 0) return; + if (symbol == 0) { + curr_code++; + last_code = 0; + } else if (symbol < 16) { + distance->lengths[curr_code] = last_code = symbol; + distance->count[symbol]++; + curr_code++; + } else if (symbol == 16) { + length = 3 + pull_bits(stream, 2); + for (;length; length--, curr_code++) { + distance->lengths[curr_code] = + last_code; + distance->count[last_code]++; + } + } else if (symbol == 17) { + curr_code += 3 + pull_bits(stream, 3); + last_code = 0; + } else { + curr_code += 11 + pull_bits(stream, 7); + last_code = 0; + } + } + fill_code_tables(distance); + + decompress_huffman(stream, dest); +} + +/* fill in the length and distance huffman codes for fixed encoding + * (section 3.2.6) */ +static void decompress_fixed(struct bitstream *stream, unsigned char *dest) +{ + /* let gcc fill in the initial values */ + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + cramfs_memset(lengths->count, 0, 16); + cramfs_memset(lengths->first, 0, 16); + cramfs_memset(lengths->lengths, 8, 144); + cramfs_memset(lengths->lengths + 144, 9, 112); + cramfs_memset(lengths->lengths + 256, 7, 24); + cramfs_memset(lengths->lengths + 280, 8, 8); + lengths->count[7] = 24; + lengths->count[8] = 152; + lengths->count[9] = 112; + + cramfs_memset(distance->count, 0, 16); + cramfs_memset(distance->first, 0, 16); + cramfs_memset(distance->lengths, 5, 32); + distance->count[5] = 32; + + + fill_code_tables(lengths); + fill_code_tables(distance); + + + decompress_huffman(stream, dest); +} + +/* returns the number of bytes decoded, < 0 if there was an error. Note that + * this function assumes that the block starts on a byte boundry + * (non-compliant, but I don't see where this would happen). section 3.2.3 */ +long decompress_block(unsigned char *dest, unsigned char *source, + void *(*inflate_memcpy)(void *, const void *, size)) +{ + int bfinal, btype; + struct bitstream stream; + + init_stream(&stream, source, inflate_memcpy); + do { + bfinal = pull_bit(&stream); + btype = pull_bits(&stream, 2); + if (btype == NO_COMP) decompress_none(&stream, dest + stream.decoded); + else if (btype == DYNAMIC_COMP) + decompress_dynamic(&stream, dest + stream.decoded); + else if (btype == FIXED_COMP) decompress_fixed(&stream, dest + stream.decoded); + else stream.error = COMP_UNKNOWN; + } while (!bfinal && !stream.error); + +#if 0 + putstr("decompress_block start\r\n"); + putLabeledWord("stream.error = ",stream.error); + putLabeledWord("stream.decoded = ",stream.decoded); + putLabeledWord("dest = ",dest); + putstr("decompress_block end\r\n"); +#endif + return stream.error ? -stream.error : stream.decoded; +} + +#endif /* CFG_CMD_JFFS2 */ |