source: umon/main/common/jffs2.c @ 87db514

Last change on this file since 87db514 was 87db514, checked in by Amar Takhar <amar@…>, on 04/16/15 at 19:26:21

Initial commit of the umon repository.

Prior to this three changes were made:

  • Remove umon_ prefix from parent directories.
  • Collapse main/target/ into main/
  • Remove ports/template/flashtest.scr.ucon script.
  • Property mode set to 100644
File size: 58.2 KB
Line 
1/**************************************************************************
2 *
3 * Copyright (c) 2013 Alcatel-Lucent
4 *
5 * Alcatel Lucent licenses this file to You under the Apache License,
6 * Version 2.0 (the "License"); you may not use this file except in
7 * compliance with the License.  A copy of the License is contained the
8 * file LICENSE at the top level of this repository.
9 * You may also obtain a copy of the License at:
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 **************************************************************************
20 *
21 * jffs2.c
22 *
23 * Reads JFFS2-formatted NOR flash.
24 *
25 * Initial version written by Ed Sutter, with contributions later made
26 * by Bill Gatliff (bgat@billgatliff.com).
27 *
28 */
29 
30#include "config.h"
31
32#if INCLUDE_JFFS2
33
34#include "stddefs.h"
35#include "assert.h"
36#include "genlib.h"
37#include "cli.h"
38#include "tfs.h"
39#include "tfsprivate.h"
40#include "time.h"
41#include "flash.h"
42
43#if INCLUDE_JFFS2ZLIB
44#include "jz_zlib.h"
45#endif
46
47#ifndef NULL
48#define NULL ((void*)0)
49#endif
50
51
52/* override JFFS2_DEFAULT_BASE in target's config.h when necessary! */
53#ifndef JFFS2_DEFAULT_BASE
54#define JFFS2_DEFAULT_BASE 0
55#endif
56
57#define JFFS2_MAXPATH 256
58
59#define JFFS2_MODE_MASK 00170000
60#define JFFS2_MODE_REG  0100000
61#define JFFS2_MODE_LNK  0120000
62#define JFFS2_MODE_BLK  0060000
63#define JFFS2_MODE_DIR  0040000
64#define JFFS2_MODE_CHR  0020000
65#define JFFS2_MODE_FIFO 0010000
66
67#define DT_REG  (JFFS2_MODE_REG  >> 12)
68#define DT_LNK  (JFFS2_MODE_LNK  >> 12)
69#define DT_BLK  (JFFS2_MODE_BLK  >> 12)
70#define DT_DIR  (JFFS2_MODE_DIR  >> 12)
71#define DT_CHR  (JFFS2_MODE_CHR  >> 12)
72#define DT_FIFO (JFFS2_MODE_FIFO >> 12)
73
74#define S_IRWXU 00700
75#define S_IRUSR 00400
76#define S_IWUSR 00200
77#define S_IXUSR 00100
78
79#define S_IRWXG 00070
80#define S_IRGRP 00040
81#define S_IWGRP 00020
82#define S_IXGRP 00010
83
84#define S_IRWXO 00007
85#define S_IROTH 00004
86#define S_IWOTH 00002
87#define S_IXOTH 00001
88
89#define S_IRUGO         (S_IRUSR|S_IRGRP|S_IROTH)
90#define S_IWUGO         (S_IWUSR|S_IWGRP|S_IWOTH)
91#define S_IXUGO         (S_IXUSR|S_IXGRP|S_IXOTH)
92
93#define is_reg(mode)    (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_REG)
94#define is_lnk(mode)    (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_LNK)
95#define is_blk(mode)    (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_BLK)
96#define is_dir(mode)    (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_DIR)
97#define is_chr(mode)    (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_CHR)
98#define is_fifo(mode)   (((mode) & JFFS2_MODE_MASK) == JFFS2_MODE_FIFO)
99
100#define JFFS2_MAGIC                     0x1985
101
102#define JFFS2_COMPAT_MASK               0xc000
103#define JFFS2_FEATURE_INCOMPAT          0xc000
104#define JFFS2_FEATURE_ROCOMPAT          0x8000
105#define JFFS2_FEATURE_RWCOMPAT_COPY     0x4000
106#define JFFS2_FEATURE_RWCOMPAT_DELETE   0x0000
107
108#define JFFS2_NODE_ACCURATE             0x2000
109
110#define JFFS2_NODETYPE_MASK             7
111#define JFFS2_NODETYPE_DIRENT           1
112#define JFFS2_NODETYPE_INODE            2
113#define JFFS2_NODETYPE_CLEANMARKER      3
114#define JFFS2_NODETYPE_PADDING          4
115
116/* this is a "fake" type, not part of jffs2 proper */
117#define JFFS2_NODETYPE_CLEANREGION      8
118
119#define JFFS2_OBSOLETE_FLAG             1
120#define JFFS2_UNUSED_FLAG               2
121
122#define JFFS2_COMPR_NONE        0       /* no compression */
123#define JFFS2_COMPR_ZERO        1       /* data is all zeroes */
124#define JFFS2_COMPR_RTIME       2
125#define JFFS2_COMPR_RUBINMIPS   3
126#define JFFS2_COMPR_COPY        4
127#define JFFS2_COMPR_DYNRUBIN    5
128#define JFFS2_COMPR_ZLIB        6
129
130#define JFFS2_FNAME_STR         "JFFS2FNAME"
131#define JFFS2_FTOT_STR          "JFFS2FTOT"
132#define JFFS2_FSIZE_STR         "JFFS2FSIZE"
133
134/* TODO: find a better way to deal with char vs. jint8 */
135typedef unsigned char jint8;
136typedef unsigned short jint16;
137typedef unsigned int jint32;
138
139struct jffs2_unknown_node {
140  jint16  magic;
141  jint16  nodetype;
142  jint32  totlen;
143  jint32  hdr_crc;
144};
145
146struct jffs2_raw_dirent {
147  jint16  magic;
148  jint16  nodetype;
149  jint32  totlen;
150  jint32  hdr_crc;
151  jint32  pino;
152  jint32  version;
153  jint32  ino;
154  jint32  mctime;
155  jint8   nsize;
156  jint8   type;
157  jint8   unused[2];
158  jint32  node_crc;
159  jint32  name_crc;
160  jint8   name[0];
161};
162
163struct jffs2_raw_inode {
164  jint16  magic;
165  jint16  nodetype;
166  jint32  totlen;
167  jint32  hdr_crc;
168  jint32  ino;
169  jint32  version;
170  jint32  mode;
171  jint16  uid;
172  jint16  gid;
173  jint32  isize;
174  jint32  atime;
175  jint32  mtime;
176  jint32  ctime;
177  jint32  offset;
178  jint32  csize;
179  jint32  dsize;
180  jint8   compr;
181  jint8   usercompr;
182  jint16  flags;
183  jint32  data_crc;
184  jint32  node_crc;
185  jint8   data[0];
186};
187
188struct jffs2_cleanregion_node {
189  jint16 magic;
190  jint16 nodetype;
191  jint32 totlen;
192  jint32 physaddr;
193};
194
195struct jffs2_umoninfo {
196  jint8   quiet;
197  jint32  direntsize;
198  char    direntname[JFFS2_MAXPATH+1];
199};
200
201/*
202 * The crc32 function used in this jffs2 command is from MTD utils.
203 *
204 *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
205 *  code or tables extracted from it, as desired without restriction.
206 *
207 *  First, the polynomial itself and its table of feedback terms.  The
208 *  polynomial is
209 *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
210 *
211 *  Note that we take it "backwards" and put the highest-order term in
212 *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
213 *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
214 *  the MSB being 1
215 *
216 *  Note that the usual hardware shift register implementation, which
217 *  is what we're using (we're merely optimizing it by doing eight-bit
218 *  chunks at a time) shifts bits into the lowest-order term.  In our
219 *  implementation, that means shifting towards the right.  Why do we
220 *  do it this way?  Because the calculated CRC must be transmitted in
221 *  order from highest-order term to lowest-order term.  UARTs transmit
222 *  characters in order from LSB to MSB.  By storing the CRC this way
223 *  we hand it to the UART in the order low-byte to high-byte; the UART
224 *  sends each low-bit to hight-bit; and the result is transmission bit
225 *  by bit from highest- to lowest-order term without requiring any bit
226 *  shuffling on our part.  Reception works similarly
227 *
228 *  The feedback terms table consists of 256, 32-bit entries.  Notes
229 *
230 *      The table can be generated at runtime if desired; code to do so
231 *      is shown later.  It might not be obvious, but the feedback
232 *      terms simply represent the results of eight shift/xor opera
233 *      tions for all combinations of data and CRC register values
234 *
235 *      The values must be right-shifted by eight bits by the "updcrc
236 *      logic; the shift must be unsigned (bring in zeroes).  On some
237 *      hardware you could probably optimize the shift in assembler by
238 *      using byte-swap instructions
239 *      polynomial $edb88320
240 */
241
242static const jint32 jffs2_crc32_table[256] = {
243  0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
244  0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
245  0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
246  0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
247  0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
248  0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
249  0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
250  0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
251  0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
252  0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
253  0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
254  0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
255  0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
256  0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
257  0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
258  0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
259  0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
260  0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
261  0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
262  0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
263  0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
264  0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
265  0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
266  0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
267  0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
268  0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
269  0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
270  0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
271  0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
272  0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
273  0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
274  0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
275  0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
276  0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
277  0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
278  0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
279  0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
280  0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
281  0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
282  0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
283  0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
284  0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
285  0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
286  0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
287  0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
288  0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
289  0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
290  0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
291  0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
292  0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
293  0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
294  0x2d02ef8dL
295};
296
297static jint32
298jffs2crc32(jint32 val, const void *ss, int len)
299{
300  const unsigned char *s = ss;
301
302  while (--len >= 0)
303    val = jffs2_crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
304
305  return val;
306}
307
308static char *
309compr2str(jint8 compr)
310{
311  switch(compr) {
312  case JFFS2_COMPR_NONE:
313    return "none";
314  case JFFS2_COMPR_ZERO:
315    return "zero";
316  case JFFS2_COMPR_RTIME:
317    return "rtime";
318  case JFFS2_COMPR_RUBINMIPS:
319    return "rubinmips";
320  case JFFS2_COMPR_COPY:
321    return "copy";
322  case JFFS2_COMPR_DYNRUBIN:
323    return "dynrubin";
324  case JFFS2_COMPR_ZLIB:
325    return "zlib";
326  default:
327    return "???";
328  }
329}
330
331static void
332showUnknown(int nodenum, struct jffs2_unknown_node *u)
333{
334  printf("Node %3d (%04x=UNKNOWN, size=%ld) 0x%08lx...\n",
335         nodenum, u->nodetype, u->totlen, (long)u);
336  printf("  type:     0x%04x\n",u->nodetype);
337}
338
339static void
340showPadding(int nodenum, struct jffs2_raw_inode *i)
341{
342  printf("Node %3d (PADDING, size=%ld) @ 0x%08lx...\n",
343         nodenum, i->totlen, (long)i);
344}
345
346static void
347showCleanmarker(int nodenum,struct jffs2_raw_inode *i)
348{
349  printf("Node %3d (CLEANMARKER, size=%ld) @ 0x%08lx...\n",
350         nodenum,i->totlen, (long)i);
351}
352
353static void
354showInode(int nodenum,struct jffs2_raw_inode *i)
355{
356  printf("Node %3d (%04x=INODE, size=%ld) @ 0x%08lx",
357         nodenum, i->nodetype, i->totlen, (long)i);
358  if (i->dsize)
359    printf(" (data at 0x%08lx)",&i->data);
360  printf("...\n");
361  printf("    ino: 0x%06lx,   mode: 0x%06lx, version: 0x%06lx\n",
362         i->ino,i->mode,i->version);
363  printf("  isize: 0x%06lx,  csize: 0x%06lx,   dsize: 0x%06lx\n",
364         i->isize, i->csize, i->dsize);
365  printf(" offset: 0x%06lx,  compr: %8s,  ucompr: %s\n",
366         i->offset,compr2str(i->compr),compr2str(i->usercompr));
367}
368
369static void
370showDirent(int nodenum, struct jffs2_raw_dirent *d)
371{
372  jint8 i;
373
374  printf("Node %3d (%04x=DIRENT <\"",nodenum,d->nodetype);
375  for(i=0;i<d->nsize;i++)
376    putchar(d->name[i]);
377  printf("\">, size=%ld)",d->totlen);
378  printf(" @ 0x%06lx",(long)d);
379  if (d->ino == 0)
380    printf(" (deleted)");
381  if ((d->nodetype & JFFS2_NODE_ACCURATE) == 0)
382    printf(" (obsolete)");
383  printf("...\n");
384  printf("    ino: 0x%06lx,   pino: 0x%06lx, version: 0x%06lx\n",
385         d->ino, d->pino, d->version);
386  printf("  nsize: 0x%06lx,   type: 0x%06lx\n",
387         d->nsize, d->type);
388}
389
390static void
391showCleanregion(int nodenum, struct jffs2_cleanregion_node *c)
392{
393  printf("Node %3d (%04x=CLEANREGION)...\n",
394         nodenum, c->nodetype);
395  printf("    physaddr: 0x%06lx,  size: %d\n",
396         c->physaddr, c->totlen);
397}
398
399
400/* jzlibcpy():
401 * A variation on "memcpy", but using JFFS2's zlib decompression...
402 */
403static int
404jzlibcpy(char *src, char *dest, ulong srclen, ulong destlen)
405{
406#if INCLUDE_JFFS2ZLIB
407  z_stream strm;
408  int ret;
409
410  if ((strm.workspace = malloc(zlib_inflate_workspacesize())) == 0)
411    return(-1);
412
413  if (Z_OK != zlib_inflateInit(&strm)) {
414    free(strm.workspace);
415    return -1;
416  }
417       
418  strm.next_in = (unsigned char *)src;
419  strm.avail_in = srclen;
420  strm.total_in = 0;
421
422  strm.next_out = (unsigned char *)dest;
423  strm.avail_out = destlen;
424  strm.total_out = 0;
425
426  while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK);
427
428  zlib_inflateEnd(&strm);
429  free(strm.workspace);
430  return(strm.total_out);
431#else
432  printf("INCLUDE_JFFS2ZLIB not set in config.h, can't decompress\n");
433  return -1;
434#endif
435}
436
437
438static int
439is_accurate_node(struct jffs2_unknown_node *u)
440{
441  return ((u->magic == JFFS2_MAGIC)
442          && (u->nodetype & JFFS2_NODE_ACCURATE)) ? 1 : 0;
443}
444
445static int
446is_cleanmarker(struct jffs2_unknown_node *u)
447{
448  jint16 nodetype = u->nodetype & JFFS2_NODETYPE_MASK;
449  return nodetype == JFFS2_NODETYPE_CLEANMARKER ? 1 : 0;
450}
451
452static int
453is_padding(struct jffs2_unknown_node *u)
454{
455  jint16 nodetype = u->nodetype & JFFS2_NODETYPE_MASK;
456  return nodetype == JFFS2_NODETYPE_PADDING ? 1 : 0;
457}
458
459static int
460is_inode(struct jffs2_unknown_node *u)
461{
462  jint16 nodetype = u->nodetype & JFFS2_NODETYPE_MASK;
463  return nodetype == JFFS2_NODETYPE_INODE ? 1 : 0;
464}
465
466static int
467is_dirent(struct jffs2_unknown_node *u)
468{
469  jint16 nodetype = u->nodetype & JFFS2_NODETYPE_MASK;
470  return nodetype == JFFS2_NODETYPE_DIRENT ? 1 : 0;
471}
472
473static int
474is_deleted_dirent(struct jffs2_unknown_node *u)
475{
476  struct jffs2_raw_dirent *d;
477
478  if (is_dirent(u)) {
479    d = (struct jffs2_raw_dirent *)u;
480    if (d->ino == 0)
481      return 1;   
482  }
483
484  return 0;
485}
486
487static int
488is_cleanregion(struct jffs2_unknown_node *u)
489{
490  return u->nodetype == JFFS2_NODETYPE_CLEANREGION ? 1 : 0;
491}
492
493static struct jffs2_unknown_node *
494allocCleanRegion(jint32 base, jint32 size)
495{
496  struct jffs2_cleanregion_node *c;
497
498  c = (struct jffs2_cleanregion_node*)malloc(sizeof(*c));
499  if (c) {
500    memset((void*)c, 0, sizeof(*c));
501    c->nodetype = JFFS2_NODETYPE_CLEANREGION;
502    c->physaddr = base;
503    c->totlen = size;
504  }
505  else
506    printf("%s: malloc() failed\n", __func__);
507  return (struct jffs2_unknown_node*)c;
508}
509
510static jint32
511calcHdrCrc(struct jffs2_unknown_node *u)
512{
513  return jffs2crc32(0, (void*)u,
514                    sizeof(*u) - sizeof(u->hdr_crc));
515}
516
517static jint32
518calcInodeCrc(struct jffs2_raw_inode *i)
519{
520  return jffs2crc32(0, (void*)i,
521                    sizeof(*i) - sizeof(i->node_crc)
522                    - sizeof(i->data_crc));
523}
524
525static jint32
526calcDirentCrc(struct jffs2_raw_dirent *d)
527{
528  return jffs2crc32(0, (void*)d,
529                    sizeof(*d) - sizeof(d->node_crc)
530                    - sizeof(d->name_crc));
531}
532
533static jint32
534calcDataCrc(void *data, int len)
535{
536  return jffs2crc32(0, data, len);
537}
538
539static int jffs2_verify_crc = 0;
540
541static struct jffs2_unknown_node *
542findRawNode(ulong jaddr, ulong jlen,
543            ulong *newjaddr, ulong *newjlen)
544{
545  struct jffs2_unknown_node *u;
546  jint32 crc;
547  ulong size = 0;
548  long adj;
549
550  while (jlen && !gotachar()) {
551
552    adj = ulceil(jaddr, 4) - jaddr;
553    jaddr += adj;
554    jlen -= adj;
555   
556    u = (struct jffs2_unknown_node *)jaddr;
557
558    if (is_accurate_node(u)) {
559
560      if (jffs2_verify_crc) {
561
562        crc = calcHdrCrc(u);
563        if (crc != u->hdr_crc)
564          printf("hdr_crc @ 0x%x: "
565                 "calculated 0x%x read 0x%x (ignored)\n",
566                 u, crc, u->hdr_crc);
567         
568        if (is_dirent(u)) {
569          struct jffs2_raw_dirent *d;
570           
571          d = (struct jffs2_raw_dirent*)u;
572          crc = calcDirentCrc(d);
573          if (crc != d->node_crc)
574            printf("dirent node_crc @ 0x%x: "
575                   "calculated 0x%x read 0x%x (ignored)\n",
576                   d, crc, d->node_crc);
577           
578          crc = calcDataCrc(d->name, d->nsize);
579          if (crc != d->name_crc)
580            printf("dirent name_crc @ 0x%x: "
581                   "calculated 0x%x read 0x%x (ignored)\n",
582                   d, crc, d->name_crc);
583        }
584         
585        else if (is_inode(u)) {
586          struct jffs2_raw_inode *i;
587           
588          i = (struct jffs2_raw_inode*)u;
589          crc = calcInodeCrc(i);
590          if (crc != i->node_crc)
591            printf("inode node_crc @ 0x%x: "
592                   "calculated 0x%x read 0x%x (ignored)\n",
593                   i, crc, i->node_crc);
594           
595          crc = calcDataCrc(i->data,
596                            (i->compr != JFFS2_COMPR_NONE) ?
597                            i->csize : i->dsize);
598          if (crc != i->data_crc)
599            printf("inode data_crc @ 0x%x: "
600                   "calculated 0x%x read 0x%x (ignored)\n",
601                   i, crc, i->data_crc);
602        }
603      }
604
605      if (newjaddr) *newjaddr = jaddr;
606      if (newjlen) *newjlen = jlen;
607      return u;
608    }
609
610    else {
611      /* manufacture a node representing unused memory */
612
613      /* TODO: more-carefully verify that this memory is truly
614         "unused", e.g. make sure there is a valid cleanmarker at the
615         beginning of the flash block, we don't span the end of a
616         flash section, etc. */
617
618      /* TODO: it would be much faster to use a binary search for ff's
619         in the current flash sector, rather than a linear search for
620         them */
621
622      for (size = 0; jlen && !gotachar()
623             && *(ulong*)jaddr == 0xffffffffUL;
624           size += 4) {
625        jaddr += 4;
626        jlen -= 4;
627      }
628
629      if (size > sizeof(struct jffs2_raw_inode)) {
630        if (newjaddr) *newjaddr = jaddr - size;
631        if (newjlen) *newjlen = jlen + size;
632        return allocCleanRegion((ulong)u, size);
633      }
634
635      if (jlen) {
636        jaddr += 4;
637        jlen -= 4;
638      }
639    }
640  }
641 
642  return NULL;
643}
644
645struct jffs2_unknown_node_list {
646  struct jffs2_unknown_node_list *next;
647  struct jffs2_unknown_node_list *prev;
648  ulong nodenum;
649  struct jffs2_unknown_node *u;
650};
651
652static int
653is_empty_list (struct jffs2_unknown_node_list *l)
654{
655  if ((l == NULL) || (l->next == NULL)
656      || (l->next->next == NULL)) return 1;
657  return 0;
658}
659
660static struct jffs2_unknown_node_list *
661allocListNode(ulong nodenum, struct jffs2_unknown_node *u)
662{
663  struct jffs2_unknown_node_list *cu;
664
665  cu = (struct jffs2_unknown_node_list*)malloc(sizeof(*cu));
666  if (cu) {
667    cu->next = NULL;
668    cu->prev = NULL;
669    cu->u = u;
670    cu->nodenum = nodenum;
671  }
672  else {
673    /* TODO: report/handle allocation failure */
674    printf("allocListNode: malloc() failed\n");
675  }
676  return cu;
677}
678
679static struct jffs2_unknown_node_list *
680removeNodeFromList(struct jffs2_unknown_node_list *u)
681{
682  if (u->prev != NULL)
683    u->prev->next = u->next;
684  if (u->next != NULL)
685    u->next->prev = u->prev;
686  return u;
687}
688
689static int
690delListNode(struct jffs2_unknown_node_list *u)
691{
692  removeNodeFromList(u);
693  if (u->u != NULL && is_cleanregion(u->u))
694    free(u->u);
695  free(u);
696  return 0;
697}
698
699static struct jffs2_unknown_node_list *
700initNodeList(struct jffs2_unknown_node_list **a)
701{
702  /* create "sentinel" nodes */
703  *a = allocListNode(-1, NULL);
704  if (*a == NULL) return NULL;
705  (*a)->next = allocListNode(-1, NULL);
706  if ((*a)->next == NULL) return NULL;
707  (*a)->next->prev = *a;
708
709  return *a;
710}
711
712static struct jffs2_unknown_node_list *
713discardNodeList(struct jffs2_unknown_node_list *list)
714{
715  if (list == NULL)
716    return NULL;
717  while (list->prev != NULL)
718    list = list->prev;
719  while (list->next != NULL)
720    delListNode(list->next);
721  delListNode(list);
722  return NULL;
723}
724
725
726
727
728static struct jffs2_unknown_node_list *
729insertListNodeAfter(struct jffs2_unknown_node_list *after,
730                    struct jffs2_unknown_node_list *n)
731{
732  n->prev = after;
733  n->next = after->next;
734  after->next->prev = n;
735  after->next = n;
736
737  return n;
738}
739
740static int
741scanMedium(ulong jaddr, ulong jlen,
742           struct jffs2_unknown_node_list *list,
743           void (*progress)(int percent))
744{
745  struct jffs2_unknown_node *u;
746  struct jffs2_unknown_node_list *n;
747  ulong nodetot = 0UL;
748  ulong jlen_orig = jlen;
749
750  if (progress) {
751    printf("scanning JFFS2 medium "
752           "at 0x%x, %d bytes...\n",
753           jaddr, jlen);
754    progress(0);
755  }
756
757  while (!gotachar()
758         && jlen
759         && (u = findRawNode(jaddr, jlen, &jaddr, &jlen))) {
760
761    if (progress)
762      progress(((unsigned long long)(jlen_orig - jlen) * 100)
763               / jlen_orig);
764       
765    if (is_inode(u)
766        || is_dirent(u)
767        || is_deleted_dirent(u)
768        || is_cleanmarker(u)
769        || is_cleanregion(u)) {
770
771      n = allocListNode(nodetot++, u);
772      if (n != NULL)
773        insertListNodeAfter(list, n);
774      else {
775        printf("%s: allocListNode returned NULL (fatal)\n",
776               __func__);
777        return -1;
778      }
779    }
780    else printf("%s: unrecognized nodetype %x (ignored)\n",
781                __func__, u->nodetype & JFFS2_NODETYPE_MASK);
782   
783    jlen -= u->totlen;
784    jaddr += u->totlen;
785  }
786
787  return nodetot;
788}
789
790static int
791formatMedium (ulong jaddr, ulong jlen,
792              void (*progress)(int percent))
793{
794  int ret;
795  int start, end, sect, size;
796  uchar *addr;
797  struct jffs2_unknown_node cm = {
798    .magic = JFFS2_MAGIC,
799    .nodetype = (JFFS2_NODETYPE_CLEANMARKER
800                 | JFFS2_FEATURE_RWCOMPAT_DELETE
801                 | JFFS2_NODE_ACCURATE),
802    .totlen = sizeof(struct jffs2_unknown_node),
803  };
804
805  if (!jlen) return -1;
806
807  cm.hdr_crc = calcHdrCrc(&cm);
808
809  if (addrtosector((uchar*)jaddr, &start,
810                   NULL, NULL)) {
811    printf("%s: invalid starting address %x (error, abort)\n",
812           __func__, jaddr);
813    return -1;
814  }
815
816  if (addrtosector((uchar*)(jaddr + jlen - 1), &end,
817                   NULL, NULL)) {
818    printf("%s: invalid ending address %x (error, abort)\n",
819           __func__, jaddr + jlen - 1);
820    return -1;
821  }
822
823  ret = sectortoaddr(start, NULL, (uchar**)(&addr));
824  if (ret || ((ulong)addr != jaddr)) {
825    printf("%s: must start at a sector boundary (abort)\n",
826           __func__);
827    return -1;
828  }
829
830  ret = sectortoaddr(end, &size, (uchar**)(&addr));
831  if (ret || ((ulong)(addr + size) != (jaddr + jlen))) {
832    printf("%s: must end at a sector boundary (abort)\n",
833           __func__);
834    return -1;
835  }
836
837  printf("formatting JFFS2 medium "
838         "at 0x%x, %d sectors, %d bytes...\n",
839         jaddr, end - start, jlen);
840
841  for (sect = start; sect < end; sect++) {
842   
843    if (progress)
844      progress(((sect - start) * 100) / (end - start));
845   
846    if ((ret = AppFlashErase(sect)) != 1) {
847      printf("%s: error %d from AppFlashErase, "
848             "sector %d (abort)\n",
849             __func__, ret, sect);
850      return -1;
851    }
852   
853    if ((ret = sectortoaddr(sect, &size, (uchar**)(&addr))) != 0) {
854      printf("%s: error %d from sectortoaddr(%d, NULL, &addr) (abort)\n",
855             __func__, ret, sect);
856      return -1;
857    }
858   
859    if (size < sizeof(cm)) {
860      printf("%s: size of sector %d "
861             "is smaller than a CLEANMARKER (abort)\n",
862             __func__, sect);
863      return -1;
864    }
865   
866    if ((ret = AppFlashWrite(addr, (void*)&cm, cm.totlen)) != 0) {
867      printf("%s: error %d from AppFlashWrite, sector %d (abort)\n",
868             __func__, ret, sect);
869      return -1;
870    }
871  }
872
873  return 0;
874}
875
876
877static int
878showNode(ulong nodenum, struct jffs2_unknown_node *u)
879{
880  if (is_inode(u))
881    showInode(nodenum, (struct jffs2_raw_inode*)u);
882  else if (is_dirent(u) || is_deleted_dirent(u))
883    showDirent(nodenum, (struct jffs2_raw_dirent*)u);
884  else if (is_cleanmarker(u))
885    showCleanmarker(nodenum, (struct jffs2_raw_inode*)u);
886  else if (is_cleanregion(u))
887    showCleanregion(nodenum, (struct jffs2_cleanregion_node*)u);
888  else if (is_padding(u))
889    showPadding(nodenum, (struct jffs2_raw_inode*)u);
890  else showUnknown(nodenum, u);
891  return 0;
892}
893
894static struct jffs2_unknown_node_list *
895findNodeOfType(struct jffs2_unknown_node_list *list,
896               jint16 nodetype)
897{
898  while (list != NULL && !gotachar()) {
899    if (list->u != NULL
900        && ((nodetype == JFFS2_NODETYPE_MASK)
901            || ((list->u->nodetype & JFFS2_NODETYPE_MASK) == nodetype)))
902      return list;
903    list = list->next;
904  }
905 
906  return NULL;
907}
908
909static struct jffs2_unknown_node_list *
910findCleanRegion(struct jffs2_unknown_node_list *list,
911                ulong minsize)
912{
913  while (list != NULL && !gotachar()) {
914    if (list->u != NULL
915        && is_cleanregion(list->u)) {
916     
917      if (list->u->totlen >= minsize)
918        return list;
919    }
920    list = list->next;
921  }
922 
923  return NULL;
924}
925
926/*
927 * Finds an inode entry in <list> where u->ino == <ino>.
928 *
929 * Returns NULL if no inode of the requested number is found. Otherwise:
930 *
931 *   <version> == -1: the highest-version entry in the list is returned
932 *   <version> != -1: the specified entry in the list is returned
933 *
934 * For ownership and mode information, you want the latest inode
935 * entry.  The only time a specific entry is interesting is when
936 * replaying the entire inode log to reconstruct its data or other
937 * history.
938 */
939static struct jffs2_unknown_node_list *
940findInode(struct jffs2_unknown_node_list *list,
941          jint32 ino, jint32 version)
942{
943  struct jffs2_raw_inode *i;
944  struct jffs2_unknown_node_list *ulatest = NULL;
945
946  while (list != NULL && !gotachar()
947         && (list = findNodeOfType(list, JFFS2_NODETYPE_INODE)) != NULL) {
948
949    i = (struct jffs2_raw_inode*)list->u;
950    if (i->ino == ino) {
951      if (version != -1 && version == i->version)
952        return list;
953      else if (version == -1) {
954        if (ulatest == NULL)
955          ulatest = list;
956        else if (i->version > ((struct jffs2_raw_inode*)(ulatest->u))->version)
957          ulatest = list;
958      }
959    }
960
961    list = list->next;
962  }
963
964  if (version == -1 && ulatest != NULL)
965    return ulatest;
966
967  return NULL;
968}
969
970/*
971 * Searches <list> for a dirent that matches <pino>, <ino>, and/or
972 * <name>.  Returns a pointer to the first matching dirent node found
973 * in <list>, including deleted dirent nodes, or NULL if a matching
974 * dirent cannot be found.
975 */
976static struct jffs2_unknown_node_list *
977findNextMatchingDirent(struct jffs2_unknown_node_list *list,
978                       jint32 pino, jint32 ino, char *name)
979{
980  struct jffs2_raw_dirent *d;
981
982  while (!gotachar()
983         && (list = findNodeOfType(list, JFFS2_NODETYPE_DIRENT)) != NULL) {
984   
985    d = (struct jffs2_raw_dirent*)list->u;
986   
987    if ((pino == -1 || (pino == d->pino))
988        && (ino == -1 || (ino == d->ino))) {
989     
990      if ((name == NULL)
991          || ((strlen(name) == d->nsize)
992              && !memcmp((char*)name, (char*)(d->name), d->nsize)))
993        return list;
994    }
995
996    list = list->next;
997  }
998
999  return NULL;
1000}
1001
1002/*
1003 * Finds the highest-version, non-deleted dirent that matches the
1004 * supplied parameters, or NULL if the highest-version dirent is a
1005 * deleted one
1006 */
1007static struct jffs2_unknown_node_list *
1008findMatchingDirent(struct jffs2_unknown_node_list *list,
1009                   jint32 pino, jint32 ino, char *name)
1010{
1011  struct jffs2_unknown_node_list *u, *uret;
1012  struct jffs2_raw_dirent *d, *dret;
1013
1014  for (uret = NULL, u = list; u != NULL
1015         && ((u = findNextMatchingDirent(u, pino, ino, name)) != NULL);
1016       u = u->next) {
1017
1018    if (uret == NULL) {
1019      uret = u;
1020      continue;
1021    }
1022   
1023    dret = (struct jffs2_raw_dirent*)uret->u;
1024    d = (struct jffs2_raw_dirent*)u->u;
1025   
1026    if (d->version > dret->version)
1027      uret = u;
1028  }
1029 
1030  if (uret == NULL)
1031    return NULL;
1032
1033  if (is_deleted_dirent(uret->u))
1034    return NULL;
1035
1036  return uret;
1037}
1038
1039static struct jffs2_unknown_node_list *
1040findNodeOfTypeRange(struct jffs2_unknown_node_list *list,
1041                    jint16 nodetype,
1042                    char *pino,
1043                    char *ino)
1044{
1045  struct jffs2_raw_dirent *d;
1046  struct jffs2_raw_inode *i;
1047
1048  while (!gotachar()
1049         && (list = findNodeOfType(list, nodetype)) != NULL) {
1050
1051    if (is_dirent(list->u)) {
1052      d = (struct jffs2_raw_dirent*)(list->u);
1053      if ((pino == NULL || inRange(pino, d->pino))
1054          && (ino == NULL || inRange(ino, d->ino)))
1055        return list;
1056    }
1057   
1058    else if (is_inode(list->u)) {
1059      i = (struct jffs2_raw_inode*)(list->u);
1060      if (ino == NULL || inRange(ino, i->ino))
1061        return list;
1062    }
1063   
1064    list = list->next;
1065  }
1066 
1067  return NULL;
1068}
1069
1070static void
1071showInodeType(int type)
1072{
1073  if      (is_reg(type))  printf("-");
1074  else if (is_dir(type))  printf("d");
1075  else if (is_lnk(type))  printf("l");
1076  else if (is_blk(type))  printf("b");
1077  else if (is_chr(type))  printf("c");
1078  else if (is_fifo(type)) printf("p");
1079  else                    printf("?");
1080}
1081
1082static void
1083showInodePerm(int perm)
1084{
1085  printf("%s", perm & S_IRUSR ? "r" : "-");
1086  printf("%s", perm & S_IWUSR ? "w" : "-");
1087  printf("%s", perm & S_IXUSR ? "x" : "-");
1088
1089  printf("%s", perm & S_IRGRP ? "r" : "-");
1090  printf("%s", perm & S_IWGRP ? "w" : "-");
1091  printf("%s", perm & S_IXGRP ? "x" : "-");
1092
1093  printf("%s", perm & S_IROTH ? "r" : "-");
1094  printf("%s", perm & S_IWOTH ? "w" : "-");
1095  printf("%s", perm & S_IXOTH ? "x" : "-");
1096}
1097
1098static int
1099showInodeMeta(struct jffs2_raw_inode *i)
1100{
1101  char buf[80];
1102  struct tm t;
1103
1104  showInodeType(i->mode);
1105  showInodePerm(i->mode);
1106  gmtime_r(i->atime, &t);
1107  asctime_r(&t, buf);
1108
1109  printf(" %4d %4d %6d %s ",
1110         i->uid, i->gid, i->isize, buf);
1111  return 0;
1112}
1113
1114static int
1115showNodesOfType(struct jffs2_unknown_node_list *list,
1116                jint16 nodetype)
1117{
1118  int count = 0;
1119
1120  while ((list = findNodeOfType(list, nodetype)) != NULL
1121         && !gotachar()) {
1122    showNode(list->nodenum, list->u);
1123    count++;
1124    list = list->next;
1125  }
1126  return count;
1127}
1128
1129static int
1130showNodesOfTypeRange(struct jffs2_unknown_node_list *list,
1131                     jint16 nodetype,
1132                     char *pino,
1133                     char *ino)
1134{
1135  int count = 0;
1136
1137  while (list && !gotachar()) {
1138    list = findNodeOfTypeRange(list, nodetype, pino, ino);
1139    if (list != NULL && list->u != NULL) {
1140      showNode(list->nodenum, list->u);
1141      count++;
1142    }
1143    if (list != NULL)
1144      list = list->next;
1145  }
1146  return count;
1147}
1148
1149static struct jffs2_unknown_node_list *
1150findNodeOfNumRange(struct jffs2_unknown_node_list *list,
1151                   char *nodenum)
1152{
1153  while (!gotachar() && list) {
1154   
1155    if (list->u && inRange(nodenum, list->nodenum))
1156      return list;
1157   
1158    list = list->next;
1159  }
1160 
1161  return NULL;
1162}
1163
1164static int
1165showNodesOfNumRange(struct jffs2_unknown_node_list *list,
1166                    char *range)
1167{
1168  int nodetot = 0;
1169
1170  while (list && (list = findNodeOfNumRange(list, range))) {
1171    nodetot++;
1172    showNode(list->nodenum, list->u);
1173    list = list->next;
1174  }
1175
1176  return nodetot;
1177}
1178
1179static struct jffs2_unknown_node_list *
1180findDirentByPath(struct jffs2_unknown_node_list *list,
1181                 char *path)
1182{
1183#define JFFS2_ROOT_PINO 1
1184#define JFFS2_PATH_SEPARATOR "/"
1185
1186  char subpath[JFFS2_MAXPATH + 1];
1187  int pathlen;
1188  jint32 pino = JFFS2_ROOT_PINO;
1189  struct jffs2_unknown_node_list *u = NULL;
1190  struct jffs2_raw_dirent *d;
1191
1192  if (strlen(path) > JFFS2_MAXPATH
1193      || (path == NULL))
1194    return NULL;
1195
1196  while (*path) {
1197
1198    path += strspn(path, JFFS2_PATH_SEPARATOR);
1199    pathlen = strcspn(path, JFFS2_PATH_SEPARATOR);
1200    strncpy(subpath, path, pathlen);
1201    subpath[pathlen] = 0;
1202    path += pathlen;
1203
1204    u = findMatchingDirent(list, pino, -1, subpath);
1205    if (u == NULL)
1206      return NULL;
1207
1208    d = (struct jffs2_raw_dirent *)u->u;
1209    pino = d->ino;
1210  }
1211
1212  if (is_deleted_dirent(u->u))
1213    return NULL;
1214  return u;
1215}
1216
1217static int
1218listDirent(struct jffs2_unknown_node_list *list,
1219           struct jffs2_unknown_node_list *u,
1220           struct jffs2_umoninfo *umip)
1221{
1222  struct jffs2_raw_dirent *d;
1223  struct jffs2_raw_inode *i;
1224
1225  d = (struct jffs2_raw_dirent*)u->u;
1226 
1227  u = findInode(list, d->ino, -1);
1228  if (u == NULL)
1229    return 0;
1230
1231  i = (struct jffs2_raw_inode*)u->u;
1232  if (!umip->quiet)
1233    showInodeMeta(i);
1234 
1235  memset(umip->direntname, 0, sizeof(umip->direntname));
1236  memcpy(umip->direntname, (void*)d->name, d->nsize);
1237  umip->direntsize = i->isize;
1238 
1239  if (is_dir(i->mode))
1240    umip->direntname[d->nsize] = '/';
1241
1242  if (!umip->quiet)
1243    printf("%s\n", umip->direntname);
1244
1245  return 1;
1246}
1247
1248static int
1249listDirentByPath(struct jffs2_unknown_node_list *list,
1250                 char *path, struct jffs2_umoninfo *umip)
1251{
1252  struct jffs2_unknown_node_list *u = NULL;
1253  struct jffs2_unknown_node_list *n;
1254  struct jffs2_raw_dirent *d = NULL;
1255  struct jffs2_raw_inode *i;
1256  jint32 pino = JFFS2_ROOT_PINO;
1257  int ret = 0;
1258 
1259  /* TODO: wildcards? */
1260 
1261  if (path != NULL && strlen(path) != 0
1262      && strcspn(path, JFFS2_PATH_SEPARATOR) != 0) {
1263
1264    u = findDirentByPath(list, path);
1265    if (u == NULL)
1266      goto not_found;
1267
1268    d = (struct jffs2_raw_dirent*)u->u;
1269    n = findInode(list, d->ino, -1);
1270    if (n == NULL)
1271      goto not_found;
1272
1273    i = (struct jffs2_raw_inode*)n->u;
1274    if (!is_dir(i->mode))
1275      return listDirent(list, u, umip);
1276    else pino = d->ino;
1277  }
1278 
1279  for (u = list; u != NULL
1280         && (u = findMatchingDirent(u, pino, -1, NULL)) != NULL;
1281       u = u->next) {
1282    ret += listDirent(list, u, umip);
1283  }
1284 
1285  return ret;
1286
1287 not_found:
1288  if (!umip->quiet)
1289    printf("%s: no such file or directory\n", path);
1290  return 0;
1291
1292}
1293
1294
1295static int
1296readRawInode(struct jffs2_raw_inode *i,
1297             jint32 offset /* ... into start of inode */,
1298             jint32 len /* ... of bytes to take from inode */,
1299             void (*callback)(void *args, int offset, void *buf, int size),
1300             void *callback_args)
1301{
1302  static void *temp = NULL;
1303
1304  /* TODO: we could allocate a 4K buffer here, read into that, and
1305     then pass that buffer to the callback (instead of having the
1306     callback read from flash directly); this would allow us to put
1307     jffs2 into something other than bulk NOR flash
1308
1309     (is there a uMON flash-reading function that handles
1310     device-specific i/o details?)
1311  */
1312  switch (i->compr) {
1313
1314  case JFFS2_COMPR_NONE:
1315    callback(callback_args, i->offset + offset,
1316             (void*)(i->data + offset), len);
1317    break;
1318
1319  case JFFS2_COMPR_ZERO:
1320    callback(callback_args, i->offset + offset, 0, len);
1321    break;
1322
1323  case JFFS2_COMPR_ZLIB:
1324    if (temp == NULL && ((temp = malloc(4096)) == NULL)) {
1325      printf("%s: cannot alloc tempspace (aborted)\n",
1326             __func__);
1327      return -1;
1328    }
1329
1330    if (i->dsize > 4096) {
1331      printf("%s: inode dsize > 4K (aborted)\n", __func__);
1332      return -1;
1333    }
1334
1335    if (jzlibcpy((void*)(i->data), temp, i->csize, i->dsize) < 0)
1336      return -1;
1337    callback(callback_args, i->offset + offset, temp + offset, len);
1338    break;
1339
1340  case JFFS2_COMPR_RTIME:
1341  case JFFS2_COMPR_RUBINMIPS:
1342  case JFFS2_COMPR_DYNRUBIN:
1343    printf("%s: unsupported compression algorithm (fragment ignored)\n",
1344           compr2str(i->compr));
1345    break;
1346  }
1347 
1348  return len;
1349}
1350
1351/*
1352 * Finds a region in <list> a.k.a. "fragment" that overlaps inode <i>,
1353 * and populates that fragment with the contents of the inode.  If the
1354 * fragment is fully covered by the contents of the inode, then the
1355 * fragment is removed from <list>; otherwise, the fragment list node
1356 * is modified or bisected to record bytes that are still missing.
1357 *
1358 * Returns the number of bytes written, or 0 to indicate there were no
1359 * fragments that overlapped the inode, or a negative number for
1360 * error.
1361 *
1362 * You'll need to call this function with the same inode until it
1363 * returns zero or an error, since we return at the first occurence of
1364 * a valid fragment.  Depending on the sequence of inodes, one or more
1365 * fragments could be covered by the same inode and/or it might take
1366 * multiple inodes to completely fill a fragment.  That's just how
1367 * jffs2 and other log-structured filesystems work.
1368 */
1369static int
1370readFrag(struct jffs2_unknown_node_list *list,
1371         struct jffs2_raw_inode *i,
1372         void (*callback)(void *args, int offset, void *buf, int size),
1373         void *callback_args)
1374{
1375  jint32 fstart, istart, fend, iend;
1376  struct jffs2_cleanregion_node *frag;
1377  struct jffs2_unknown_node *newfrag;
1378
1379  for ( ; list->next != NULL; list = list->next) {
1380   
1381    /* we "shouldn't" have to do this, but since we're emptying the
1382       list entirely the list pointer is always aimed at the sentinel
1383       node; skip it if we find it, and use the ->next == NULL case to
1384       spot the end of the list (contrary to the rest of the code in
1385       this compilation unit) */
1386    if (list->u == NULL) continue;
1387
1388    frag = (struct jffs2_cleanregion_node*)list->u;
1389
1390    fstart = frag->physaddr;
1391    istart = i->offset;
1392    fend = frag->physaddr + frag->totlen;
1393    iend = i->offset + i->dsize;
1394
1395    if (fend <= istart || iend <= fstart)
1396      continue;
1397
1398    if (fstart >= istart && fend <= iend) {
1399      delListNode(list);
1400      return readRawInode(i, fstart - istart, fend - fstart,
1401                          callback, callback_args);
1402    }
1403
1404    if (fstart <= istart && fend <= iend) {
1405      frag->totlen = istart - fstart;
1406      return readRawInode(i, 0, fend - istart,
1407                          callback, callback_args);
1408    }
1409
1410    if (fstart >= istart && fend >= iend) {
1411      frag->physaddr = iend;
1412      frag->totlen = fend - iend;
1413      return readRawInode(i, fstart - istart, iend - fstart,
1414                          callback, callback_args);
1415    }
1416 
1417    frag->totlen = iend - fend;
1418    newfrag = allocCleanRegion(iend, fend - iend);
1419    insertListNodeAfter(list, allocListNode(0, newfrag));
1420
1421    return readRawInode(i, 0, i->dsize, callback, callback_args);
1422  }
1423
1424  return 0;
1425}
1426
1427
1428static int
1429readInode(struct jffs2_unknown_node_list *list,
1430          jint32 ino,
1431          void (*callback)(void *callback_args,
1432                           int offset, void *buf, int size),
1433          void *callback_args)
1434{
1435  struct jffs2_unknown_node_list *frags;
1436  struct jffs2_unknown_node_list *u;
1437  struct jffs2_unknown_node *fr;
1438  struct jffs2_unknown_node_list *f;
1439  struct jffs2_raw_inode *i;
1440  int ret;
1441  int len = 0;
1442
1443  if ((u = findInode(list, ino, -1)) == NULL) {
1444    printf("%d: no such inode\n", ino);
1445    return -1;
1446  }
1447
1448  i = (struct jffs2_raw_inode*)u->u;
1449  if (i->isize == 0)
1450    return 0;
1451
1452  if (initNodeList(&frags) == NULL) {
1453    printf("%s: initNodeList() returned NULL (aborted)\n",
1454           __func__);
1455    return -1;
1456  }
1457
1458  if ((fr = allocCleanRegion(0, i->isize)) == NULL)
1459    goto fail_alloc_initial_region;
1460  if ((f = allocListNode(0, fr)) == NULL)
1461    goto fail_alloc_initial_node;
1462  insertListNodeAfter(frags, f);
1463
1464  /*
1465   * Note: The convention in most of this compilation unit is for list
1466   * pointers to point to the first valid list member.  However, in
1467   * the case of the fragment list we know the caller is going to be
1468   * removing nodes, including the first valid list member.  Thus,
1469   * we'll keep the list pointer on the sentinel node since that one
1470   * never goes away.  (The readFrag() iterator knows about this, and
1471   * changes its list walking logic accordingly).
1472   */
1473
1474  do {
1475    i = (struct jffs2_raw_inode*)u->u;
1476    do {
1477      ret = readFrag(frags, i, callback, callback_args);
1478      if (ret >= 0) len += ret;
1479    } while (ret > 0);
1480
1481    u = findInode(list, ino, i->version - 1);
1482  } while (!is_empty_list(frags) && u != NULL);
1483
1484  if (!is_empty_list(frags))
1485    printf("%s: warning, nonempty fragment list "
1486           "(missing inode?) (ignored)\n");
1487
1488  while(frags->next)
1489    delListNode(frags->next);
1490  delListNode(frags);
1491
1492  return len;
1493
1494 fail_alloc_initial_node:
1495  free(fr);
1496  printf("%s: allocListNode() returned NULL (aborted)\n",
1497         __func__);
1498  return 0;
1499 fail_alloc_initial_region:
1500  printf("%s: allocCleanRegion() returned NULL (aborted)\n",
1501         __func__);
1502  return 0;
1503}
1504
1505static int
1506readDirent(struct jffs2_unknown_node_list *list, char *path,
1507           void (*callback)(void *args,
1508                            int offset, void *buf, int size),
1509           void *callback_args)
1510{
1511  struct jffs2_unknown_node_list *u;
1512  struct jffs2_raw_dirent *d;
1513  struct jffs2_raw_inode *i;
1514
1515  u = findDirentByPath(list, path);
1516  if (u == NULL)
1517    goto no_such_file;
1518 
1519  d = (struct jffs2_raw_dirent *)u->u;
1520  u = findInode(list, d->ino, -1);
1521  if (u == NULL)
1522    goto no_such_inode;
1523
1524  i = (struct jffs2_raw_inode*)u->u;
1525  if (!is_reg(i->mode))
1526    goto not_a_file;
1527
1528  return readInode(list, d->ino, callback, callback_args);
1529 
1530 no_such_file:
1531  printf("%s: no such file or directory\n", path);
1532  return -1;
1533
1534 no_such_inode:
1535  printf("%d: no such inode\n", d->ino);
1536  return -1;
1537
1538 not_a_file:
1539  printf("%s: is %s\n", path,
1540         is_dir(i->mode) ? "a directory" : "not a file");
1541  return -1;
1542}
1543
1544static struct jffs2_unknown_node *
1545commitRawNode(struct jffs2_unknown_node_list *list,
1546              struct jffs2_unknown_node *u)
1547{
1548  struct jffs2_unknown_node_list *c;
1549  unsigned long adjtotlen;
1550  int ret;
1551
1552  /* everything is 32-bit aligned in jffs2-land, so you always need a
1553     region slightly larger than the data itself to accomodate the
1554     address and length rounding we may apply later on */
1555  c = findCleanRegion(list, u->totlen + 4);
1556  if (c != NULL) {
1557
1558    struct jffs2_cleanregion_node *f = (struct jffs2_cleanregion_node*)c->u;
1559
1560    ret = AppFlashWrite((void*)(f->physaddr), (void*)u, u->totlen);
1561    if (ret != 0)
1562      printf("%s: AppFlashWrite returned %d (ignored)\n", __func__, ret);
1563
1564    ret = f->physaddr;
1565
1566    /* cleanregions must always begin on a 32-bit boundary */
1567    adjtotlen = ulceil(u->totlen, 4);
1568    f->totlen -= adjtotlen;
1569    f->physaddr += adjtotlen;
1570
1571    return (struct jffs2_unknown_node*)ret;
1572  }
1573
1574  return NULL;
1575}
1576
1577static struct jffs2_raw_dirent *
1578allocDirentNode (char *name)
1579{
1580  struct jffs2_raw_dirent *d;
1581  int namelen = 0;
1582
1583  if (name != NULL)
1584    namelen = strlen(name);
1585
1586  d = (struct jffs2_raw_dirent*)malloc(sizeof(*d) + namelen);
1587  if (d == NULL) {
1588    printf("%s: malloc() returned NULL (aborted)\n", __func__);
1589    return NULL;
1590  }
1591
1592  memset((void*)d, 0, sizeof(*d));
1593
1594  d->magic = JFFS2_MAGIC;
1595  d->nodetype = JFFS2_NODETYPE_DIRENT
1596    | JFFS2_NODE_ACCURATE
1597    | JFFS2_FEATURE_ROCOMPAT
1598    | JFFS2_FEATURE_RWCOMPAT_COPY;
1599  memcpy((void*)d->name, name, d->nsize = namelen);
1600  d->totlen = sizeof(*d) + d->nsize;
1601
1602  return d;
1603}
1604
1605static void
1606freeDirentNode(struct jffs2_raw_dirent *d)
1607{
1608  free(d);
1609}
1610
1611
1612static jint32
1613nextPinoVersion(struct jffs2_unknown_node_list *list, jint32 pino)
1614{
1615  jint32 version = 0;
1616
1617  while(list) {
1618    if (list->u && is_dirent(list->u)) {
1619      struct jffs2_raw_dirent *d = (struct jffs2_raw_dirent*)list->u;
1620     
1621      if (d->pino == pino && d->version > version)
1622        version = d->version;
1623    }
1624    list = list->next;
1625  }
1626
1627  return version + 1;
1628}
1629
1630static struct jffs2_unknown_node_list *
1631commitDirent(struct jffs2_unknown_node_list *list, ulong nodenum,
1632             char *name, jint8 type, jint32 pino, jint32 ino,
1633             jint32 version)
1634{
1635  struct jffs2_raw_dirent *d;
1636  struct jffs2_unknown_node *dc;
1637  struct jffs2_unknown_node_list *n;
1638
1639  d = allocDirentNode(name);
1640  if (d == NULL)
1641    return NULL;
1642
1643  d->type = type;
1644  d->version = version;
1645  d->pino = pino;
1646  d->ino = ino;
1647
1648  d->hdr_crc = calcHdrCrc((struct jffs2_unknown_node *)d);
1649  d->node_crc = calcDirentCrc(d);
1650  d->name_crc = calcDataCrc(d->name, d->nsize);
1651
1652  dc = commitRawNode(list, (struct jffs2_unknown_node*)d);
1653  freeDirentNode(d);
1654
1655  if (dc == NULL) /* TODO: error handling? */
1656    return NULL;
1657 
1658  if ((n = allocListNode(nodenum, dc)) != NULL)
1659    return insertListNodeAfter(list, n);
1660
1661  return NULL;
1662}
1663
1664static struct jffs2_unknown_node_list *
1665moveDirent(struct jffs2_unknown_node_list *list,
1666           int nodetot, char *oldpath, char *newpath)
1667{
1668  struct jffs2_unknown_node_list *u, *ui, *uold, *unew;
1669  struct jffs2_raw_dirent *d = NULL;
1670  struct jffs2_raw_inode *i;
1671  jint32 pino;
1672  jint32 nextver;
1673
1674  char newname[JFFS2_MAXPATH + 1];
1675
1676  uold = findDirentByPath(list, oldpath);
1677  if (uold == NULL)
1678    goto no_such;
1679  d = (struct jffs2_raw_dirent*)uold->u;
1680
1681  unew = findDirentByPath(list, newpath);
1682  if (unew != NULL) {
1683    struct jffs2_raw_dirent *dnew;
1684
1685    dnew = (struct jffs2_raw_dirent*)unew->u;
1686    ui = findInode(list, dnew->ino, -1);
1687    i = (struct jffs2_raw_inode*)ui->u;
1688    if (!is_dir(i->mode))
1689      goto in_the_way;
1690
1691    pino = dnew->ino;
1692    strncpy(newname, (void*)d->name, d->nsize);
1693    newname[d->nsize] = 0;
1694  }
1695  else {
1696    strcpy(newname, newpath);
1697    pino = JFFS2_ROOT_PINO;
1698  }
1699 
1700  nextver = nextPinoVersion(list, pino);
1701  u = commitDirent(list, nodetot++, newname,
1702                   d->type, pino, d->ino, nextver++);
1703  /* TODO: error handling? */
1704  u = commitDirent(list, nodetot++, oldpath,
1705                   d->type, d->pino, 0, nextver);
1706  return u;
1707 
1708 no_such:
1709  printf("%s: no such file or directory\n", oldpath);
1710  return NULL;
1711
1712 in_the_way:
1713  /* (yes, we could delete this ourselves...) */
1714  printf("%s already exists\n", newpath);
1715  return NULL;
1716}
1717
1718static struct jffs2_unknown_node_list *
1719deleteDirent(struct jffs2_unknown_node_list *list,
1720             int nodetot, char *path)
1721{
1722  struct jffs2_unknown_node_list *u, *ui;
1723  struct jffs2_raw_dirent *d;
1724  struct jffs2_raw_inode *i;
1725  jint32 nextver;
1726  char name[JFFS2_MAXPATH + 1];
1727
1728  u = findDirentByPath(list, path);
1729  if (u == NULL)
1730    goto no_such;
1731  d = (struct jffs2_raw_dirent*)u->u;
1732
1733  ui = findInode(list, d->ino, -1);
1734  i = (struct jffs2_raw_inode*)ui->u;
1735  if (is_dir(i->mode)) {
1736    /* TODO: make sure directory is empty */
1737    printf("%s: is a directory (abort)\n", path);
1738    return NULL;
1739  }
1740
1741  strncpy(name, (void*)d->name, d->nsize);
1742  name[d->nsize] = 0;
1743
1744  nextver = nextPinoVersion(list, d->pino);
1745  u = commitDirent(list, nodetot++, name,
1746                   d->type, d->pino, 0, nextver);
1747
1748  return u;
1749 
1750 no_such:
1751  printf("%s: no such file or directory\n", path);
1752  return NULL;
1753}
1754
1755#define JFFS2_DEFAULT_UID 0
1756#define JFFS2_DEFAULT_GID 0
1757
1758static struct jffs2_raw_inode *
1759allocInode(jint32 ino, jint32 dsize)
1760{
1761  struct jffs2_raw_inode *i;
1762
1763  i = (struct jffs2_raw_inode*)malloc(sizeof(*i) + dsize);
1764  if (i == NULL) {
1765    printf("%s: malloc() returned NULL (aborted)\n", __func__);
1766    return NULL;
1767  }
1768
1769  memset((void*)i, 0, sizeof(*i) + dsize);
1770
1771  i->magic = JFFS2_MAGIC;
1772  i->ino = ino;
1773  i->nodetype = JFFS2_NODETYPE_INODE
1774    | JFFS2_NODE_ACCURATE
1775    | JFFS2_FEATURE_ROCOMPAT
1776    | JFFS2_FEATURE_RWCOMPAT_COPY;
1777  i->compr = JFFS2_COMPR_NONE;
1778  i->dsize = i->csize = dsize;
1779  i->totlen = sizeof(*i) + dsize;
1780
1781  return i; 
1782}
1783
1784static void
1785freeInode(struct jffs2_raw_inode *i)
1786{
1787  free(i);
1788}
1789
1790static jint32
1791getUnusedIno(struct jffs2_unknown_node_list *list)
1792{
1793  /* by definition, the root pino is always taken... */
1794  jint32 ino = JFFS2_ROOT_PINO + 1;
1795
1796  while (!gotachar()
1797         && (list = findNodeOfType(list, JFFS2_NODETYPE_INODE)) != NULL) {
1798    struct jffs2_raw_inode *i = (struct jffs2_raw_inode*)list->u;
1799    if (i->ino > ino)
1800      ino = i->ino + 1;
1801    list = list->next;
1802  }
1803
1804  if (gotachar())
1805    return -1;
1806  return ino;
1807}
1808
1809
1810
1811/* TODO: deleteDirectory() a.k.a. rmdir */
1812
1813static struct jffs2_unknown_node_list *
1814appendInode(struct jffs2_unknown_node_list *list,
1815            int nodenum,
1816            char *path,
1817            void *data,
1818            int len)
1819{
1820  struct jffs2_unknown_node_list *n;
1821  struct jffs2_raw_inode *iprev, *inew;
1822  struct jffs2_raw_dirent *d;
1823  struct jffs2_unknown_node *ic;
1824
1825  /* TODO: break append up into smaller nodes, if there isn't a single
1826     region large enough */
1827
1828  /* TODO: don't deal with large appends yet (nodes are not permitted
1829     to exceed 4K in size per jffs2) */
1830  if (len >= 4096) {
1831    printf("inode extension by >= 4K not supported (yet)\n");
1832    return NULL;
1833  }
1834
1835  n = findDirentByPath(list, path);
1836  if (n == NULL)
1837    goto not_found;
1838  d = (struct jffs2_raw_dirent*)n->u;
1839
1840  n = findInode(list, d->ino, -1);
1841  if (n == NULL)
1842    goto not_found;
1843  iprev = (struct jffs2_raw_inode*)n->u;
1844
1845  inew = allocInode(iprev->ino, len);
1846  if (inew == NULL)
1847    return NULL;
1848
1849  inew->ino = iprev->ino;
1850  inew->version = iprev->version + 1;
1851  inew->mode = iprev->mode;
1852  inew->uid = iprev->uid;
1853  inew->gid = iprev->gid;
1854  inew->atime = iprev->atime;
1855  inew->mtime = iprev->mtime;
1856  inew->ctime = iprev->ctime;
1857
1858  inew->offset = inew->isize;
1859  inew->isize += len;
1860  inew->compr = JFFS2_COMPR_NONE;
1861  inew->usercompr = 0;
1862  memcpy((void*)inew->data, data, len);
1863
1864  inew->hdr_crc = calcHdrCrc((struct jffs2_unknown_node*)inew);
1865  inew->node_crc = calcInodeCrc(inew);
1866  inew->data_crc = calcDataCrc(inew->data, inew->dsize);
1867
1868  ic = commitRawNode(list, (struct jffs2_unknown_node*)inew);
1869  freeInode(inew);
1870
1871  if (ic == NULL) /* TODO: error handling? */
1872    return NULL;
1873
1874  if ((n = allocListNode(nodenum++, ic)) != NULL)
1875    return insertListNodeAfter(list, n);
1876
1877  return NULL;
1878
1879 not_found:
1880  printf("%s: no such file or directory\n", path);
1881  return NULL;
1882}
1883
1884static struct jffs2_unknown_node_list *
1885createInode(struct jffs2_unknown_node_list *list,
1886            int nodenum,
1887            jint32 mode,
1888            char *path)
1889{
1890  struct jffs2_unknown_node_list *n;
1891  struct jffs2_raw_inode *i;
1892  struct jffs2_unknown_node *ic;
1893  char *ppath = path;
1894  jint32 pino = 1;
1895  jint32 ino;
1896  jint8 type;
1897  char name[JFFS2_MAXPATH + 1];
1898
1899  n = findDirentByPath(list, path);
1900  if (n != NULL)
1901    goto already_exists;
1902
1903  while (*ppath && strstr(ppath, JFFS2_PATH_SEPARATOR) != NULL)
1904    ppath++;
1905
1906  if (ppath != path) {
1907    struct jffs2_raw_dirent *d;
1908    strncpy(name, path, ppath - path - 1);
1909    name[ppath - path - 1] = 0;
1910
1911    n = findDirentByPath(list, name);
1912    if (n == NULL)
1913      goto not_found;
1914    d = (struct jffs2_raw_dirent*)n->u;
1915    pino = d->ino;
1916  }
1917
1918  ino = getUnusedIno(list);
1919  if (ino == -1)
1920    return NULL;
1921
1922  i = allocInode(ino, 0);
1923  if (i == NULL)
1924    return NULL;
1925
1926  i->mode = mode;
1927  i->uid = JFFS2_DEFAULT_UID;
1928  i->gid = JFFS2_DEFAULT_GID;
1929  i->version = 1;
1930
1931  i->hdr_crc = calcHdrCrc((struct jffs2_unknown_node*)i);
1932  i->node_crc = calcInodeCrc(i);
1933  i->data_crc = 0;
1934
1935  ic = commitRawNode(list, (struct jffs2_unknown_node*)i);
1936  freeInode(i);
1937
1938  if (ic == NULL) /* TODO: error handling? */
1939    return NULL;
1940 
1941  if ((n = allocListNode(nodenum++, ic)) == NULL
1942      || insertListNodeAfter(list, n) == NULL)
1943    return NULL;
1944 
1945  if (is_lnk(mode)) type = DT_LNK;
1946  else if (is_blk(mode)) type = DT_BLK;
1947  else if (is_dir(mode)) type = DT_DIR;
1948  else if (is_chr(mode)) type = DT_CHR;
1949  else if (is_fifo(mode)) type = DT_FIFO;
1950  else type = DT_REG;
1951
1952  i = (struct jffs2_raw_inode*)ic;
1953 
1954  return commitDirent(list, nodenum, ppath, type,
1955                      pino, i->ino, nextPinoVersion(list, pino));
1956
1957 already_exists:
1958  printf("%s: file exists\n", path);
1959  return NULL;
1960
1961 not_found:
1962  printf("%s: no such file or directory\n", name);
1963  return NULL;
1964}
1965
1966struct jffs2_callback_args {
1967  void *dest;
1968};
1969
1970static void
1971callback_to_memory (void *vargs, int offset,
1972                    void *src, int size)
1973{
1974  struct jffs2_callback_args *args = vargs;
1975  if (src == NULL)
1976    memset(args->dest + offset, 0, size);
1977  else
1978    memcpy(args->dest + offset, src, size);
1979}
1980
1981static void
1982callback_to_console (void *vargs, int offset,
1983                     void *src, int size)
1984{
1985  char *cp = src;
1986
1987  if (src == NULL)
1988    return;
1989
1990  for (cp = src; size > 0; size--)
1991    putchar(*cp++);
1992}
1993
1994char *Jffs2Help[] = {
1995  "Read and write JFFS2-formatted flash space",
1996  "[b:cs:] {operation} [args]...",
1997#if INCLUDE_VERBOSEHELP
1998  "",
1999  "Options:",
2000  " -b {addr}    base address of JFFS2 space (note 1)",
2001  " -c           check CRCs of all nodes (note 2)",
2002  " -s {size}    size of JFFS2 space {note 4}",
2003  " -q           quiet operation",
2004  "",
2005  "Operations:",
2006  " add   {fname} {address} {bytes}",
2007  "              append {bytes} bytes at {address} to file {fname} (note 3)",
2008  " cat   {fname}  dump contents of ASCII file {fname} to console",
2009  " cp    {fname} {to_fname}",
2010  "              copy a file {fname} to {to_fname}",
2011  " get   {fname} {to_addr}",
2012  "              dump contents of file {fname} to {to_addr}",
2013  " ls    [path]   list files and/or dirs of the specified [path]",
2014  " mkdir {path} create a directory named {path}",
2015  " mv    {path} {to_path}",
2016  "              relocate a file/directory from {path} to {to_path}",
2017  " rm    {fname}  delete file named {fname}",
2018  " mkfs  [{{addr} {size}}]",
2019  "              build an empty JFFS2 filesystem (notes 1,4)",
2020  "",
2021  "Additional operations:",
2022  " dump         display all nodes",
2023  " dirent       display all dirent nodes",
2024  " inode        display all inode nodes",
2025  " ino   [rng]  display nodes with specified ino field",
2026  " node  [rng]  display nodes of specified numerical range",
2027  " pino  [rng]  display nodes with specified pino field",
2028  " rescan       discard current node list, if any, then scan",
2029  " scan         scan filesystem image only",
2030
2031#if 0
2032  " zinf {src} {dest} {srclen} {destlen}",
2033  "              inflate using JFFS2's zlib",
2034#endif
2035  "",
2036  "Notes:",
2037  "   1.  Base address defaults to $JFFS2BASE or zero, if not specified",
2038  "   2.  Defaults to off",
2039  "   3.  If {fname} does not exist, it is created",
2040  "   4.  Size defaults to $JFFS2SIZE or zero, if not specified",
2041#endif
2042  0
2043};
2044
2045static void
2046progress_callback(int percent)
2047{
2048  static int prev_percent;
2049
2050  if (prev_percent == percent)
2051    return;
2052
2053  prev_percent = percent;
2054  printf("\r%3d%%\r",
2055         percent > 100 ? 100 : percent);
2056}
2057
2058int
2059Jffs2Cmd(int argc, char *argv[])
2060{
2061  ulong jaddr, jsize;
2062  struct jffs2_unknown_node *u;
2063  int opt;
2064  char *env, *cmd, *fname, *range;
2065  int bytes, nodes;
2066  void (*progress)(int percent) = progress_callback;
2067
2068  static int nodetot = 0;
2069  static struct jffs2_unknown_node_list *nodeList = NULL;
2070
2071  jint32 jffs2_base = JFFS2_DEFAULT_BASE;
2072  jint32 jffs2_size = 0;
2073
2074  assert(sizeof(jint32) == 4);
2075  assert(sizeof(jint16) == 2);
2076  assert(sizeof(jint8) == 1);
2077
2078  jffs2_verify_crc = 0;
2079
2080  if ((env = getenv("JFFS2BASE")))
2081    jffs2_base = (jint32)strtoul(env,0,0);
2082
2083  if ((env = getenv("JFFS2SIZE")))
2084    jffs2_size = (jint32)strtoul(env,0,0);
2085
2086  while ((opt=getopt(argc,argv,"b:cs:q")) != -1) {
2087    switch(opt) {
2088    case 'b':
2089      jffs2_base = (jint32)strtoul(optarg,0,0);
2090      break;
2091    case 'c':
2092      jffs2_verify_crc = 1;
2093      break;
2094    case 's':
2095      jffs2_size = (jint32)strtoul(optarg,0,0);
2096      break;
2097    case 'q':
2098      progress = NULL;
2099      break;
2100    default:
2101      return(CMD_PARAM_ERROR);
2102    }
2103  }
2104
2105  if (argc < optind + 1)
2106    return(CMD_PARAM_ERROR);
2107
2108  cmd = argv[optind];
2109
2110  if (strcmp(cmd, "mkfs") == 0) {
2111    if (argc <= optind + 1)
2112      jaddr = jffs2_base;
2113    else
2114      jaddr = strtoul(argv[optind + 1], 0, 0);
2115    if (argc <= optind + 2)
2116      jsize = jffs2_size;
2117    else
2118      jsize = strtoul(argv[optind + 2], 0, 0);
2119
2120    nodeList = discardNodeList(nodeList);
2121
2122    /* TODO: progress feedback needs to be optional? */
2123    return formatMedium(jaddr, jsize, progress) ?
2124      CMD_FAILURE : CMD_SUCCESS;
2125  }
2126
2127  if (strcmp(cmd, "rescan") == 0)
2128    nodeList = discardNodeList(nodeList);
2129
2130  /* commands below this point require a scanMedium() */
2131
2132  if (nodeList == NULL) {
2133    initNodeList(&nodeList);
2134    if (nodeList != NULL) {
2135      nodetot = scanMedium(jffs2_base, jffs2_size, nodeList, progress);
2136      while (nodeList->prev)
2137        nodeList = nodeList->prev;
2138    }
2139  }
2140
2141  if (strcmp(cmd, "scan") == 0
2142      || strcmp(cmd, "rescan") == 0)
2143    return CMD_SUCCESS;
2144
2145  jaddr = jffs2_base;
2146  u = (struct jffs2_unknown_node *)jaddr;
2147 
2148  if (strcmp(cmd,"dump") == 0) {
2149    printf("JFFS2 base: 0x%lx\n",jffs2_base);
2150    printf("JFFS2 size: 0x%lx\n",jffs2_size);   
2151    nodes = showNodesOfNumRange(nodeList, "all");
2152    printf("%d nodes\n", nodes);
2153    return CMD_SUCCESS;
2154  }
2155  else if (strcmp(cmd,"node") == 0) {
2156    if (argc == optind + 1)
2157      range = "all";
2158    else
2159      range = argv[optind + 1];
2160    nodes = showNodesOfNumRange(nodeList, range);
2161    return CMD_SUCCESS;
2162  }
2163
2164  /* if you want a range of dirents or inodes, you need to specify the
2165     ino and/or pino; don't use "dirent" or "inode" */
2166  else if (strcmp(cmd,"dirent") == 0) {
2167    nodes = showNodesOfType(nodeList, JFFS2_NODETYPE_DIRENT);
2168    printf("%d nodes\n", nodes);
2169  }
2170  else if (strcmp(cmd,"inode") == 0) {
2171    nodes = showNodesOfType(nodeList, JFFS2_NODETYPE_INODE);
2172    printf("%d nodes\n", nodes);
2173  }
2174
2175  /* TODO: this can be reimplemented just like "ino"? */
2176  else if (strcmp(cmd,"pino") == 0) {
2177    jint32 pino;
2178    struct jffs2_unknown_node_list *u = nodeList;
2179   
2180    if (argc != optind+2)
2181      return CMD_PARAM_ERROR;
2182   
2183    pino = strtol(argv[optind+1],0,0);
2184   
2185    do {
2186      u = findNextMatchingDirent(u, pino, -1, NULL);
2187      if (u != NULL && u->u != NULL) {
2188        showNode(u->nodenum, u->u);
2189        u = u->next;
2190      }
2191    } while (u != NULL && u->u != NULL);
2192  }
2193
2194  else if (strcmp(cmd,"ino") == 0) {
2195
2196    if (argc == optind+1)
2197      range = "all";
2198    else
2199      range = argv[optind+1];
2200
2201    nodes = showNodesOfTypeRange(nodeList,
2202                                 JFFS2_NODETYPE_DIRENT, NULL, range);
2203    nodes += showNodesOfTypeRange(nodeList,
2204                                  JFFS2_NODETYPE_INODE, NULL, range);
2205    printf("%d nodes\n", nodes);
2206    return CMD_SUCCESS;
2207  }
2208
2209  else if (strcmp(cmd,"cp") == 0) {
2210    printf("TODO: jffs2 cp {fname} {to_fname}\n"
2211           "(it's harder than it sounds!)\n");
2212    return CMD_SUCCESS;
2213  }
2214
2215  else if (strcmp(cmd,"get") == 0) {
2216    struct jffs2_callback_args args;
2217
2218    if (argc == (optind + 3))
2219      args.dest = (void*)strtoul(argv[optind + 2], 0, 0);
2220    else
2221      return CMD_PARAM_ERROR;
2222
2223    setenv(JFFS2_FSIZE_STR,0);
2224    fname = argv[optind + 1];
2225    bytes = readDirent(nodeList, fname, callback_to_memory, &args);
2226    if (bytes >= 0)
2227      shell_sprintf(JFFS2_FSIZE_STR,"%d",bytes);
2228    printf("%d bytes\n", bytes);
2229    return CMD_SUCCESS;
2230  }
2231
2232  else if (strcmp(cmd,"cat") == 0) {
2233    if (argc != optind + 2)
2234      return CMD_PARAM_ERROR;
2235   
2236    setenv(JFFS2_FSIZE_STR,0);
2237    fname = argv[optind + 1];
2238    bytes = readDirent(nodeList, fname, callback_to_console, NULL);
2239    if (bytes >= 0)
2240      shell_sprintf(JFFS2_FSIZE_STR,"%d",bytes);
2241    return CMD_SUCCESS;
2242  }
2243
2244  else if (strcmp(cmd, "mkdir") == 0) {
2245    if (argc != optind + 2)
2246      return CMD_PARAM_ERROR;
2247
2248    createInode(nodeList, nodetot++,
2249                JFFS2_MODE_DIR
2250                | S_IRUGO | S_IXOTH | S_IXGRP | S_IRWXU,
2251                argv[optind + 1]);
2252
2253    return CMD_SUCCESS;
2254  }
2255
2256  else if (strcmp(cmd, "chmod") == 0) {
2257    printf("TODO: implement chmod\n");
2258    return CMD_SUCCESS;
2259  }
2260 
2261  else if (strcmp(cmd, "add") == 0) {
2262    if (argc != (optind + 4))
2263      return CMD_PARAM_ERROR;
2264
2265    char *path;
2266    long addr, len;
2267
2268    path = argv[optind + 1];
2269    addr = strtol(argv[optind + 2], 0, 0);
2270    len = strtol(argv[optind + 3], 0, 0);
2271
2272    if (findDirentByPath(nodeList, path) == NULL)
2273      createInode(nodeList, nodetot++,
2274                  JFFS2_MODE_REG | S_IRUGO | S_IWUSR,
2275                  path);
2276
2277    appendInode(nodeList, nodetot++, path, (void*)addr, len);
2278    return CMD_SUCCESS;
2279  }
2280
2281  else if (strcmp(cmd, "mv") == 0 && (argc == (optind + 3))) {
2282    char *oldname, *newname;
2283
2284    oldname = argv[optind + 1];
2285    newname = argv[optind + 2];
2286
2287    moveDirent(nodeList, nodetot, oldname, newname);
2288    return CMD_SUCCESS;
2289  }
2290 
2291  else if(strcmp(cmd, "rm") == 0 && (argc == (optind + 2))) {
2292    deleteDirent(nodeList, nodetot, argv[optind + 1]);
2293    return CMD_SUCCESS;
2294  }
2295
2296  else if ((strcmp(cmd, "ls") == 0) || (strcmp(cmd, "qry") == 0)) {
2297    int ftot;
2298    char *path;
2299    struct jffs2_umoninfo ju;
2300
2301    /* If the sub-command is "qry", then we do essentially the same
2302     * thing as "ls" except quietly (just populate the shell variables).
2303     */
2304    if (cmd[0] == 'q')
2305      ju.quiet = 1;
2306    else
2307      ju.quiet = 0;
2308
2309    ju.direntsize = -1;
2310    ju.direntname[0] = 0;
2311    setenv(JFFS2_FNAME_STR,0);
2312    setenv(JFFS2_FSIZE_STR,0);
2313
2314    if (argc == optind + 2)
2315      path = argv[optind + 1];
2316    else
2317      path = NULL;
2318    ftot =  listDirentByPath(nodeList, path, &ju);
2319    if ((ftot > 0) && (!ju.quiet))
2320      printf(" Total: %d\n",ftot);
2321
2322    shell_sprintf(JFFS2_FTOT_STR,"%d",ftot);
2323
2324    if (ju.direntsize > 0) {
2325      shell_sprintf(JFFS2_FSIZE_STR,"%d",ju.direntsize);
2326      setenv(JFFS2_FNAME_STR,ju.direntname);
2327    }
2328
2329    return CMD_SUCCESS;
2330  }
2331
2332  printf("jffs2 cmd <%s> not found\n",cmd);
2333  return CMD_FAILURE;
2334}
2335
2336#endif
Note: See TracBrowser for help on using the repository browser.