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 */ |
---|
135 | typedef unsigned char jint8; |
---|
136 | typedef unsigned short jint16; |
---|
137 | typedef unsigned int jint32; |
---|
138 | |
---|
139 | struct jffs2_unknown_node { |
---|
140 | jint16 magic; |
---|
141 | jint16 nodetype; |
---|
142 | jint32 totlen; |
---|
143 | jint32 hdr_crc; |
---|
144 | }; |
---|
145 | |
---|
146 | struct 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 | |
---|
163 | struct 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 | |
---|
188 | struct jffs2_cleanregion_node { |
---|
189 | jint16 magic; |
---|
190 | jint16 nodetype; |
---|
191 | jint32 totlen; |
---|
192 | jint32 physaddr; |
---|
193 | }; |
---|
194 | |
---|
195 | struct 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 | |
---|
242 | static 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 | |
---|
297 | static jint32 |
---|
298 | jffs2crc32(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 | |
---|
308 | static char * |
---|
309 | compr2str(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 | |
---|
331 | static void |
---|
332 | showUnknown(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 | |
---|
339 | static void |
---|
340 | showPadding(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 | |
---|
346 | static void |
---|
347 | showCleanmarker(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 | |
---|
353 | static void |
---|
354 | showInode(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 | |
---|
369 | static void |
---|
370 | showDirent(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 | |
---|
390 | static void |
---|
391 | showCleanregion(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 | */ |
---|
403 | static int |
---|
404 | jzlibcpy(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 | |
---|
438 | static int |
---|
439 | is_accurate_node(struct jffs2_unknown_node *u) |
---|
440 | { |
---|
441 | return ((u->magic == JFFS2_MAGIC) |
---|
442 | && (u->nodetype & JFFS2_NODE_ACCURATE)) ? 1 : 0; |
---|
443 | } |
---|
444 | |
---|
445 | static int |
---|
446 | is_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 | |
---|
452 | static int |
---|
453 | is_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 | |
---|
459 | static int |
---|
460 | is_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 | |
---|
466 | static int |
---|
467 | is_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 | |
---|
473 | static int |
---|
474 | is_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 | |
---|
487 | static int |
---|
488 | is_cleanregion(struct jffs2_unknown_node *u) |
---|
489 | { |
---|
490 | return u->nodetype == JFFS2_NODETYPE_CLEANREGION ? 1 : 0; |
---|
491 | } |
---|
492 | |
---|
493 | static struct jffs2_unknown_node * |
---|
494 | allocCleanRegion(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 | |
---|
510 | static jint32 |
---|
511 | calcHdrCrc(struct jffs2_unknown_node *u) |
---|
512 | { |
---|
513 | return jffs2crc32(0, (void*)u, |
---|
514 | sizeof(*u) - sizeof(u->hdr_crc)); |
---|
515 | } |
---|
516 | |
---|
517 | static jint32 |
---|
518 | calcInodeCrc(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 | |
---|
525 | static jint32 |
---|
526 | calcDirentCrc(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 | |
---|
533 | static jint32 |
---|
534 | calcDataCrc(void *data, int len) |
---|
535 | { |
---|
536 | return jffs2crc32(0, data, len); |
---|
537 | } |
---|
538 | |
---|
539 | static int jffs2_verify_crc = 0; |
---|
540 | |
---|
541 | static struct jffs2_unknown_node * |
---|
542 | findRawNode(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 | |
---|
645 | struct 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 | |
---|
652 | static int |
---|
653 | is_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 | |
---|
660 | static struct jffs2_unknown_node_list * |
---|
661 | allocListNode(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 | |
---|
679 | static struct jffs2_unknown_node_list * |
---|
680 | removeNodeFromList(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 | |
---|
689 | static int |
---|
690 | delListNode(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 | |
---|
699 | static struct jffs2_unknown_node_list * |
---|
700 | initNodeList(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 | |
---|
712 | static struct jffs2_unknown_node_list * |
---|
713 | discardNodeList(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 | |
---|
728 | static struct jffs2_unknown_node_list * |
---|
729 | insertListNodeAfter(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 | |
---|
740 | static int |
---|
741 | scanMedium(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 | |
---|
790 | static int |
---|
791 | formatMedium (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 | |
---|
877 | static int |
---|
878 | showNode(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 | |
---|
894 | static struct jffs2_unknown_node_list * |
---|
895 | findNodeOfType(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 | |
---|
909 | static struct jffs2_unknown_node_list * |
---|
910 | findCleanRegion(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 | */ |
---|
939 | static struct jffs2_unknown_node_list * |
---|
940 | findInode(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 | */ |
---|
976 | static struct jffs2_unknown_node_list * |
---|
977 | findNextMatchingDirent(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 | */ |
---|
1007 | static struct jffs2_unknown_node_list * |
---|
1008 | findMatchingDirent(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 | |
---|
1039 | static struct jffs2_unknown_node_list * |
---|
1040 | findNodeOfTypeRange(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 | |
---|
1070 | static void |
---|
1071 | showInodeType(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 | |
---|
1082 | static void |
---|
1083 | showInodePerm(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 | |
---|
1098 | static int |
---|
1099 | showInodeMeta(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 | |
---|
1114 | static int |
---|
1115 | showNodesOfType(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 | |
---|
1129 | static int |
---|
1130 | showNodesOfTypeRange(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 | |
---|
1149 | static struct jffs2_unknown_node_list * |
---|
1150 | findNodeOfNumRange(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 | |
---|
1164 | static int |
---|
1165 | showNodesOfNumRange(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 | |
---|
1179 | static struct jffs2_unknown_node_list * |
---|
1180 | findDirentByPath(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 | |
---|
1217 | static int |
---|
1218 | listDirent(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 | |
---|
1248 | static int |
---|
1249 | listDirentByPath(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 | |
---|
1295 | static int |
---|
1296 | readRawInode(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 | */ |
---|
1369 | static int |
---|
1370 | readFrag(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 | |
---|
1428 | static int |
---|
1429 | readInode(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 | |
---|
1505 | static int |
---|
1506 | readDirent(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 | |
---|
1544 | static struct jffs2_unknown_node * |
---|
1545 | commitRawNode(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 | |
---|
1577 | static struct jffs2_raw_dirent * |
---|
1578 | allocDirentNode (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 | |
---|
1605 | static void |
---|
1606 | freeDirentNode(struct jffs2_raw_dirent *d) |
---|
1607 | { |
---|
1608 | free(d); |
---|
1609 | } |
---|
1610 | |
---|
1611 | |
---|
1612 | static jint32 |
---|
1613 | nextPinoVersion(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 | |
---|
1630 | static struct jffs2_unknown_node_list * |
---|
1631 | commitDirent(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 | |
---|
1664 | static struct jffs2_unknown_node_list * |
---|
1665 | moveDirent(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 | |
---|
1718 | static struct jffs2_unknown_node_list * |
---|
1719 | deleteDirent(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 | |
---|
1758 | static struct jffs2_raw_inode * |
---|
1759 | allocInode(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 | |
---|
1784 | static void |
---|
1785 | freeInode(struct jffs2_raw_inode *i) |
---|
1786 | { |
---|
1787 | free(i); |
---|
1788 | } |
---|
1789 | |
---|
1790 | static jint32 |
---|
1791 | getUnusedIno(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 | |
---|
1813 | static struct jffs2_unknown_node_list * |
---|
1814 | appendInode(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 | |
---|
1884 | static struct jffs2_unknown_node_list * |
---|
1885 | createInode(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 | |
---|
1966 | struct jffs2_callback_args { |
---|
1967 | void *dest; |
---|
1968 | }; |
---|
1969 | |
---|
1970 | static void |
---|
1971 | callback_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 | |
---|
1981 | static void |
---|
1982 | callback_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 | |
---|
1994 | char *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 | |
---|
2045 | static void |
---|
2046 | progress_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 | |
---|
2058 | int |
---|
2059 | Jffs2Cmd(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 |
---|