OSDN Git Service

Add MS7619SE
[uclinux-h8/uClinux-dist.git] / user / mtd-utils / jffs2reader.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * jffs2reader v0.0.18 A jffs2 image reader
4  *
5  * Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi>
6  *
7  * This software is provided 'as-is', without any express or implied
8  * warranty. In no event will the author be held liable for any damages
9  * arising from the use of this software.
10  *
11  * Permission is granted to anyone to use this software for any
12  * purpose, including commercial applications, and to alter it and
13  * redistribute it freely, subject to the following restrictions:
14  *
15  * 1. The origin of this software must not be misrepresented; you must
16  * not claim that you wrote the original software. If you use this
17  * software in a product, an acknowledgment in the product
18  * documentation would be appreciated but is not required.
19  *
20  * 2. Altered source versions must be plainly marked as such, and must
21  * not be misrepresented as being the original software.
22  *
23  * 3. This notice may not be removed or altered from any source
24  * distribution.
25  *
26  *
27  *********
28  *  This code was altered September 2001
29  *  Changes are Copyright (c) Erik Andersen <andersen@codepoet.org>
30  *
31  * In compliance with (2) above, this is hereby marked as an altered
32  * version of this software.  It has been altered as follows:
33  *      *) Listing a directory now mimics the behavior of 'ls -l'
34  *      *) Support for recursive listing has been added
35  *      *) Without options, does a recursive 'ls' on the whole filesystem
36  *      *) option parsing now uses getopt()
37  *      *) Now uses printf, and error messages go to stderr.
38  *      *) The copyright notice has been cleaned up and reformatted
39  *      *) The code has been reformatted
40  *      *) Several twisty code paths have been fixed so I can understand them.
41  *  -Erik, 1 September 2001
42  *
43  *      *) Made it show major/minor numbers for device nodes
44  *      *) Made it show symlink targets
45  *  -Erik, 13 September 2001
46  */
47
48
49 /*
50 TODO:
51
52 - Add CRC checking code to places marked with XXX.
53 - Add support for other node compression types.
54
55 - Test with real life images.
56 - Maybe port into bootloader.
57  */
58
59 /*
60 BUGS:
61
62 - Doesn't check CRC checksums.
63  */
64
65 #define PROGRAM_NAME "jffs2reader"
66
67 #include <errno.h>
68 #include <stdint.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <fcntl.h>
74 #include <time.h>
75 #include <sys/types.h>
76 #include <sys/stat.h>
77 #include <sys/param.h>
78 #include <dirent.h>
79 #include <linux/jffs2.h>
80 #include "common.h"
81
82 #define SCRATCH_SIZE (5*1024*1024)
83
84 #ifndef MAJOR
85 /* FIXME:  I am using illicit insider knowledge of
86  * kernel major/minor representation...  */
87 #define MAJOR(dev) (((dev)>>8)&0xff)
88 #define MINOR(dev) ((dev)&0xff)
89 #endif
90
91
92 #define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0)
93 #define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0)
94
95 struct dir {
96         struct dir *next;
97         uint8_t type;
98         uint8_t nsize;
99         uint32_t ino;
100         char name[256];
101 };
102
103 void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *);
104 struct dir *putdir(struct dir *, struct jffs2_raw_dirent *);
105 void printdir(char *o, size_t size, struct dir *d, char *path,
106                 int recurse);
107 void freedir(struct dir *);
108
109 struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino);
110 struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t,
111                 char *, uint8_t);
112 struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t);
113 struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t);
114
115 struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *,
116                 uint32_t *, int);
117 struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *,
118                 uint32_t *);
119
120 void lsdir(char *, size_t, char *, int);
121 void catfile(char *, size_t, char *, char *, size_t, size_t *);
122
123 int main(int, char **);
124
125 /* writes file node into buffer, to the proper position. */
126 /* reading all valid nodes in version order reconstructs the file. */
127
128 /*
129    b       - buffer
130    bsize   - buffer size
131    rsize   - result size
132    n       - node
133  */
134
135 void putblock(char *b, size_t bsize, size_t * rsize,
136                 struct jffs2_raw_inode *n)
137 {
138         uLongf dlen = n->dsize;
139
140         if (n->isize > bsize || (n->offset + dlen) > bsize)
141                 errmsg_die("File does not fit into buffer!");
142
143         if (*rsize < n->isize)
144                 bzero(b + *rsize, n->isize - *rsize);
145
146         switch (n->compr) {
147                 case JFFS2_COMPR_ZLIB:
148                         uncompress((Bytef *) b + n->offset, &dlen,
149                                         (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
150                                         (uLongf) n->csize);
151                         break;
152
153                 case JFFS2_COMPR_NONE:
154                         memcpy(b + n->offset,
155                                         ((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
156                         break;
157
158                 case JFFS2_COMPR_ZERO:
159                         bzero(b + n->offset, dlen);
160                         break;
161
162                         /* [DYN]RUBIN support required! */
163
164                 default:
165                         errmsg_die("Unsupported compression method!");
166         }
167
168         *rsize = n->isize;
169 }
170
171 /* adds/removes directory node into dir struct. */
172 /* reading all valid nodes in version order reconstructs the directory. */
173
174 /*
175    dd      - directory struct being processed
176    n       - node
177
178    return value: directory struct value replacing dd
179  */
180
181 struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n)
182 {
183         struct dir *o, *d, *p;
184
185         o = dd;
186
187         if (n->ino) {
188                 if (dd == NULL) {
189                         d = xmalloc(sizeof(struct dir));
190                         d->type = n->type;
191                         memcpy(d->name, n->name, n->nsize);
192                         d->nsize = n->nsize;
193                         d->ino = n->ino;
194                         d->next = NULL;
195
196                         return d;
197                 }
198
199                 while (1) {
200                         if (n->nsize == dd->nsize &&
201                                         !memcmp(n->name, dd->name, n->nsize)) {
202                                 dd->type = n->type;
203                                 dd->ino = n->ino;
204
205                                 return o;
206                         }
207
208                         if (dd->next == NULL) {
209                                 dd->next = xmalloc(sizeof(struct dir));
210                                 dd->next->type = n->type;
211                                 memcpy(dd->next->name, n->name, n->nsize);
212                                 dd->next->nsize = n->nsize;
213                                 dd->next->ino = n->ino;
214                                 dd->next->next = NULL;
215
216                                 return o;
217                         }
218
219                         dd = dd->next;
220                 }
221         } else {
222                 if (dd == NULL)
223                         return NULL;
224
225                 if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) {
226                         d = dd->next;
227                         free(dd);
228                         return d;
229                 }
230
231                 while (1) {
232                         p = dd;
233                         dd = dd->next;
234
235                         if (dd == NULL)
236                                 return o;
237
238                         if (n->nsize == dd->nsize &&
239                                         !memcmp(n->name, dd->name, n->nsize)) {
240                                 p->next = dd->next;
241                                 free(dd);
242
243                                 return o;
244                         }
245                 }
246         }
247 }
248
249
250 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
251 #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
252
253 /* The special bits. If set, display SMODE0/1 instead of MODE0/1 */
254 static const mode_t SBIT[] = {
255         0, 0, S_ISUID,
256         0, 0, S_ISGID,
257         0, 0, S_ISVTX
258 };
259
260 /* The 9 mode bits to test */
261 static const mode_t MBIT[] = {
262         S_IRUSR, S_IWUSR, S_IXUSR,
263         S_IRGRP, S_IWGRP, S_IXGRP,
264         S_IROTH, S_IWOTH, S_IXOTH
265 };
266
267 static const char MODE1[] = "rwxrwxrwx";
268 static const char MODE0[] = "---------";
269 static const char SMODE1[] = "..s..s..t";
270 static const char SMODE0[] = "..S..S..T";
271
272 /*
273  * Return the standard ls-like mode string from a file mode.
274  * This is static and so is overwritten on each call.
275  */
276 const char *mode_string(int mode)
277 {
278         static char buf[12];
279
280         int i;
281
282         buf[0] = TYPECHAR(mode);
283         for (i = 0; i < 9; i++) {
284                 if (mode & SBIT[i])
285                         buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i];
286                 else
287                         buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i];
288         }
289         return buf;
290 }
291
292 /* prints contents of directory structure */
293
294 /*
295    d       - dir struct
296  */
297
298 void printdir(char *o, size_t size, struct dir *d, char *path, int recurse)
299 {
300         char m;
301         char *filetime;
302         time_t age;
303         struct jffs2_raw_inode *ri;
304
305         if (!path)
306                 return;
307         if (strlen(path) == 1 && *path == '/')
308                 path++;
309
310         while (d != NULL) {
311                 switch (d->type) {
312                         case DT_REG:
313                                 m = ' ';
314                                 break;
315
316                         case DT_FIFO:
317                                 m = '|';
318                                 break;
319
320                         case DT_CHR:
321                                 m = ' ';
322                                 break;
323
324                         case DT_BLK:
325                                 m = ' ';
326                                 break;
327
328                         case DT_DIR:
329                                 m = '/';
330                                 break;
331
332                         case DT_LNK:
333                                 m = ' ';
334                                 break;
335
336                         case DT_SOCK:
337                                 m = '=';
338                                 break;
339
340                         default:
341                                 m = '?';
342                 }
343                 ri = find_raw_inode(o, size, d->ino);
344                 if (!ri) {
345                         warnmsg("bug: raw_inode missing!");
346                         d = d->next;
347                         continue;
348                 }
349
350                 filetime = ctime((const time_t *) &(ri->ctime));
351                 age = time(NULL) - ri->ctime;
352                 printf("%s %-4d %-8d %-8d ", mode_string(ri->mode),
353                                 1, ri->uid, ri->gid);
354                 if ( d->type==DT_BLK || d->type==DT_CHR ) {
355                         dev_t rdev;
356                         size_t devsize;
357                         putblock((char*)&rdev, sizeof(rdev), &devsize, ri);
358                         printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev));
359                 } else {
360                         printf("%9ld ", (long)ri->dsize);
361                 }
362                 d->name[d->nsize]='\0';
363                 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
364                         /* hh:mm if less than 6 months old */
365                         printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m);
366                 } else {
367                         printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m);
368                 }
369                 if (d->type == DT_LNK) {
370                         char symbuf[1024];
371                         size_t symsize;
372                         putblock(symbuf, sizeof(symbuf), &symsize, ri);
373                         symbuf[symsize] = 0;
374                         printf(" -> %s", symbuf);
375                 }
376                 printf("\n");
377
378                 if (d->type == DT_DIR && recurse) {
379                         char *tmp;
380                         tmp = xmalloc(BUFSIZ);
381                         sprintf(tmp, "%s/%s", path, d->name);
382                         lsdir(o, size, tmp, recurse);           /* Go recursive */
383                         free(tmp);
384                 }
385
386                 d = d->next;
387         }
388 }
389
390 /* frees memory used by directory structure */
391
392 /*
393    d       - dir struct
394  */
395
396 void freedir(struct dir *d)
397 {
398         struct dir *t;
399
400         while (d != NULL) {
401                 t = d->next;
402                 free(d);
403                 d = t;
404         }
405 }
406
407 /* collects directory/file nodes in version order. */
408
409 /*
410    f       - file flag.
411    if zero, collect file, compare ino to inode
412    otherwise, collect directory, compare ino to parent inode
413    o       - filesystem image pointer
414    size    - size of filesystem image
415    ino     - inode to compare against. see f.
416
417    return value: a jffs2_raw_inode that corresponds the the specified
418    inode, or NULL
419  */
420
421 struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino)
422 {
423         /* aligned! */
424         union jffs2_node_union *n;
425         union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
426         union jffs2_node_union *lr;     /* last block position */
427         union jffs2_node_union *mp = NULL;      /* minimum position */
428
429         uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
430
431         vmin = 0;                                       /* next to read */
432         vmax = ~((uint32_t) 0);         /* last to read */
433         vmint = ~((uint32_t) 0);
434         vmaxt = 0;                                      /* found maximum */
435         vcur = 0;                                       /* XXX what is smallest version number used? */
436         /* too low version number can easily result excess log rereading */
437
438         n = (union jffs2_node_union *) o;
439         lr = n;
440
441         do {
442                 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
443                         ((char *) n) += 4;
444
445                 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
446                         if (n->u.nodetype == JFFS2_NODETYPE_INODE &&
447                                         n->i.ino == ino && (v = n->i.version) > vcur) {
448                                 /* XXX crc check */
449
450                                 if (vmaxt < v)
451                                         vmaxt = v;
452                                 if (vmint > v) {
453                                         vmint = v;
454                                         mp = n;
455                                 }
456
457                                 if (v == (vcur + 1))
458                                         return (&(n->i));
459                         }
460
461                         ((char *) n) += ((n->u.totlen + 3) & ~3);
462                 } else
463                         n = (union jffs2_node_union *) o;       /* we're at the end, rewind to the beginning */
464
465                 if (lr == n) {                  /* whole loop since last read */
466                         vmax = vmaxt;
467                         vmin = vmint;
468                         vmint = ~((uint32_t) 0);
469
470                         if (vcur < vmax && vcur < vmin)
471                                 return (&(mp->i));
472                 }
473         } while (vcur < vmax);
474
475         return NULL;
476 }
477
478 /* collects dir struct for selected inode */
479
480 /*
481    o       - filesystem image pointer
482    size    - size of filesystem image
483    pino    - inode of the specified directory
484    d       - input directory structure
485
486    return value: result directory structure, replaces d.
487  */
488
489 struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d)
490 {
491         /* aligned! */
492         union jffs2_node_union *n;
493         union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
494         union jffs2_node_union *lr;     /* last block position */
495         union jffs2_node_union *mp = NULL;      /* minimum position */
496
497         uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
498
499         vmin = 0;                                       /* next to read */
500         vmax = ~((uint32_t) 0);         /* last to read */
501         vmint = ~((uint32_t) 0);
502         vmaxt = 0;                                      /* found maximum */
503         vcur = 0;                                       /* XXX what is smallest version number used? */
504         /* too low version number can easily result excess log rereading */
505
506         n = (union jffs2_node_union *) o;
507         lr = n;
508
509         do {
510                 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
511                         ((char *) n) += 4;
512
513                 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
514                         if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
515                                         n->d.pino == ino && (v = n->d.version) > vcur) {
516                                 /* XXX crc check */
517
518                                 if (vmaxt < v)
519                                         vmaxt = v;
520                                 if (vmint > v) {
521                                         vmint = v;
522                                         mp = n;
523                                 }
524
525                                 if (v == (vcur + 1)) {
526                                         d = putdir(d, &(n->d));
527
528                                         lr = n;
529                                         vcur++;
530                                         vmint = ~((uint32_t) 0);
531                                 }
532                         }
533
534                         ((char *) n) += ((n->u.totlen + 3) & ~3);
535                 } else
536                         n = (union jffs2_node_union *) o;       /* we're at the end, rewind to the beginning */
537
538                 if (lr == n) {                  /* whole loop since last read */
539                         vmax = vmaxt;
540                         vmin = vmint;
541                         vmint = ~((uint32_t) 0);
542
543                         if (vcur < vmax && vcur < vmin) {
544                                 d = putdir(d, &(mp->d));
545
546                                 lr = n =
547                                         (union jffs2_node_union *) (((char *) mp) +
548                                                         ((mp->u.totlen + 3) & ~3));
549
550                                 vcur = vmin;
551                         }
552                 }
553         } while (vcur < vmax);
554
555         return d;
556 }
557
558
559
560 /* resolve dirent based on criteria */
561
562 /*
563    o       - filesystem image pointer
564    size    - size of filesystem image
565    ino     - if zero, ignore,
566    otherwise compare against dirent inode
567    pino    - if zero, ingore,
568    otherwise compare against parent inode
569    and use name and nsize as extra criteria
570    name    - name of wanted dirent, used if pino!=0
571    nsize   - length of name of wanted dirent, used if pino!=0
572
573    return value: pointer to relevant dirent structure in
574    filesystem image or NULL
575  */
576
577 struct jffs2_raw_dirent *resolvedirent(char *o, size_t size,
578                 uint32_t ino, uint32_t pino,
579                 char *name, uint8_t nsize)
580 {
581         /* aligned! */
582         union jffs2_node_union *n;
583         union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
584
585         struct jffs2_raw_dirent *dd = NULL;
586
587         uint32_t vmax, v;
588
589         if (!pino && ino <= 1)
590                 return dd;
591
592         vmax = 0;
593
594         n = (union jffs2_node_union *) o;
595
596         do {
597                 while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
598                         ((char *) n) += 4;
599
600                 if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
601                         if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
602                                         (!ino || n->d.ino == ino) &&
603                                         (v = n->d.version) > vmax &&
604                                         (!pino || (n->d.pino == pino &&
605                                                            nsize == n->d.nsize &&
606                                                            !memcmp(name, n->d.name, nsize)))) {
607                                 /* XXX crc check */
608
609                                 if (vmax < v) {
610                                         vmax = v;
611                                         dd = &(n->d);
612                                 }
613                         }
614
615                         ((char *) n) += ((n->u.totlen + 3) & ~3);
616                 } else
617                         return dd;
618         } while (1);
619 }
620
621 /* resolve name under certain parent inode to dirent */
622
623 /*
624    o       - filesystem image pointer
625    size    - size of filesystem image
626    pino    - requested parent inode
627    name    - name of wanted dirent
628    nsize   - length of name of wanted dirent
629
630    return value: pointer to relevant dirent structure in
631    filesystem image or NULL
632  */
633
634 struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino,
635                 char *name, uint8_t nsize)
636 {
637         return resolvedirent(o, size, 0, pino, name, nsize);
638 }
639
640 /* resolve inode to dirent */
641
642 /*
643    o       - filesystem image pointer
644    size    - size of filesystem image
645    ino     - compare against dirent inode
646
647    return value: pointer to relevant dirent structure in
648    filesystem image or NULL
649  */
650
651 struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino)
652 {
653         return resolvedirent(o, size, ino, 0, NULL, 0);
654 }
655
656 /* resolve slash-style path into dirent and inode.
657    slash as first byte marks absolute path (root=inode 1).
658    . and .. are resolved properly, and symlinks are followed.
659  */
660
661 /*
662    o       - filesystem image pointer
663    size    - size of filesystem image
664    ino     - root inode, used if path is relative
665    p       - path to be resolved
666    inos    - result inode, zero if failure
667    recc    - recursion count, to detect symlink loops
668
669    return value: pointer to dirent struct in file system image.
670    note that root directory doesn't have dirent struct
671    (return value is NULL), but it has inode (*inos=1)
672  */
673
674 struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino,
675                 char *p, uint32_t * inos, int recc)
676 {
677         struct jffs2_raw_dirent *dir = NULL;
678
679         int d = 1;
680         uint32_t tino;
681
682         char *next;
683
684         char *path, *pp;
685
686         char symbuf[1024];
687         size_t symsize;
688
689         if (recc > 16) {
690                 /* probably symlink loop */
691                 *inos = 0;
692                 return NULL;
693         }
694
695         pp = path = strdup(p);
696
697         if (*path == '/') {
698                 path++;
699                 ino = 1;
700         }
701
702         if (ino > 1) {
703                 dir = resolveinode(o, size, ino);
704
705                 ino = DIRENT_INO(dir);
706         }
707
708         next = path - 1;
709
710         while (ino && next != NULL && next[1] != 0 && d) {
711                 path = next + 1;
712                 next = strchr(path, '/');
713
714                 if (next != NULL)
715                         *next = 0;
716
717                 if (*path == '.' && path[1] == 0)
718                         continue;
719                 if (*path == '.' && path[1] == '.' && path[2] == 0) {
720                         if (DIRENT_PINO(dir) == 1) {
721                                 ino = 1;
722                                 dir = NULL;
723                         } else {
724                                 dir = resolveinode(o, size, DIRENT_PINO(dir));
725                                 ino = DIRENT_INO(dir);
726                         }
727
728                         continue;
729                 }
730
731                 dir = resolvename(o, size, ino, path, (uint8_t) strlen(path));
732
733                 if (DIRENT_INO(dir) == 0 ||
734                                 (next != NULL &&
735                                  !(dir->type == DT_DIR || dir->type == DT_LNK))) {
736                         free(pp);
737
738                         *inos = 0;
739
740                         return NULL;
741                 }
742
743                 if (dir->type == DT_LNK) {
744                         struct jffs2_raw_inode *ri;
745                         ri = find_raw_inode(o, size, DIRENT_INO(dir));
746                         putblock(symbuf, sizeof(symbuf), &symsize, ri);
747                         symbuf[symsize] = 0;
748
749                         tino = ino;
750                         ino = 0;
751
752                         dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc);
753
754                         if (dir != NULL && next != NULL &&
755                                         !(dir->type == DT_DIR || dir->type == DT_LNK)) {
756                                 free(pp);
757
758                                 *inos = 0;
759                                 return NULL;
760                         }
761                 }
762                 if (dir != NULL)
763                         ino = DIRENT_INO(dir);
764         }
765
766         free(pp);
767
768         *inos = ino;
769
770         return dir;
771 }
772
773 /* resolve slash-style path into dirent and inode.
774    slash as first byte marks absolute path (root=inode 1).
775    . and .. are resolved properly, and symlinks are followed.
776  */
777
778 /*
779    o       - filesystem image pointer
780    size    - size of filesystem image
781    ino     - root inode, used if path is relative
782    p       - path to be resolved
783    inos    - result inode, zero if failure
784
785    return value: pointer to dirent struct in file system image.
786    note that root directory doesn't have dirent struct
787    (return value is NULL), but it has inode (*inos=1)
788  */
789
790 struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino,
791                 char *p, uint32_t * inos)
792 {
793         return resolvepath0(o, size, ino, p, inos, 0);
794 }
795
796 /* lists files on directory specified by path */
797
798 /*
799    o       - filesystem image pointer
800    size    - size of filesystem image
801    p       - path to be resolved
802  */
803
804 void lsdir(char *o, size_t size, char *path, int recurse)
805 {
806         struct jffs2_raw_dirent *dd;
807         struct dir *d = NULL;
808
809         uint32_t ino;
810
811         dd = resolvepath(o, size, 1, path, &ino);
812
813         if (ino == 0 ||
814                         (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR))
815                 errmsg_die("%s: No such file or directory", path);
816
817         d = collectdir(o, size, ino, d);
818         printdir(o, size, d, path, recurse);
819         freedir(d);
820 }
821
822 /* writes file specified by path to the buffer */
823
824 /*
825    o       - filesystem image pointer
826    size    - size of filesystem image
827    p       - path to be resolved
828    b       - file buffer
829    bsize   - file buffer size
830    rsize   - file result size
831  */
832
833 void catfile(char *o, size_t size, char *path, char *b, size_t bsize,
834                 size_t * rsize)
835 {
836         struct jffs2_raw_dirent *dd;
837         struct jffs2_raw_inode *ri;
838         uint32_t ino;
839
840         dd = resolvepath(o, size, 1, path, &ino);
841
842         if (ino == 0)
843                 errmsg_die("%s: No such file or directory", path);
844
845         if (dd == NULL || dd->type != DT_REG)
846                 errmsg_die("%s: Not a regular file", path);
847
848         ri = find_raw_inode(o, size, ino);
849         putblock(b, bsize, rsize, ri);
850
851         write(1, b, *rsize);
852 }
853
854 /* usage example */
855
856 int main(int argc, char **argv)
857 {
858         int fd, opt, recurse = 0;
859         struct stat st;
860
861         char *scratch, *dir = NULL, *file = NULL;
862         size_t ssize = 0;
863
864         char *buf;
865
866         while ((opt = getopt(argc, argv, "rd:f:")) > 0) {
867                 switch (opt) {
868                         case 'd':
869                                 dir = optarg;
870                                 break;
871                         case 'f':
872                                 file = optarg;
873                                 break;
874                         case 'r':
875                                 recurse++;
876                                 break;
877                         default:
878                                 fprintf(stderr,
879                                                 "Usage: %s <image> [-d|-f] < path >\n",
880                                                 PROGRAM_NAME);
881                                 exit(EXIT_FAILURE);
882                 }
883         }
884
885         fd = open(argv[optind], O_RDONLY);
886         if (fd == -1)
887                 sys_errmsg_die("%s", argv[optind]);
888
889         if (fstat(fd, &st))
890                 sys_errmsg_die("%s", argv[optind]);
891
892         buf = xmalloc((size_t) st.st_size);
893
894         if (read(fd, buf, st.st_size) != (ssize_t) st.st_size)
895                 sys_errmsg_die("%s", argv[optind]);
896
897         if (dir)
898                 lsdir(buf, st.st_size, dir, recurse);
899
900         if (file) {
901                 scratch = xmalloc(SCRATCH_SIZE);
902
903                 catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
904                 free(scratch);
905         }
906
907         if (!dir && !file)
908                 lsdir(buf, st.st_size, "/", 1);
909
910
911         free(buf);
912         exit(EXIT_SUCCESS);
913 }