1 /*
2 * File isodump.c - dump iso9660 directory information.
3 *
4
5 Written by Eric Youngdale (1993).
6
7 Copyright 1993 Yggdrasil Computing, Incorporated
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
22
23 static char rcsid[] ="$Id: isoinfo.c,v 1.1 2000/10/10 20:40:28 beck Exp $";
24
25 /*
26 * Simple program to dump contents of iso9660 image in more usable format.
27 *
28 * Usage:
29 * To list contents of image (with or without RR):
30 * isoinfo -l [-R] -i imagefile
31 * To extract file from image:
32 * isoinfo -i imagefile -x xtractfile > outfile
33 * To generate a "find" like list of files:
34 * isoinfo -f -i imagefile
35 */
36
37 #include "../config.h"
38
39 #include <stdio.h>
40 #include <signal.h>
41 #include <sys/stat.h>
42
43 #include "../iso9660.h"
44
45 #ifdef __SVR4
46 #include <stdlib.h>
47 #else
48 extern int optind;
49 extern char *optarg;
50 /* extern int getopt (int __argc, char **__argv, char *__optstring); */
51 #endif
52
53 #ifndef S_ISLNK
54 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
55 #endif
56 #ifndef S_ISSOCK
57 # ifdef S_IFSOCK
58 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
59 # else
60 # define S_ISSOCK(m) (0)
61 # endif
62 #endif
63
64 FILE * infile;
65 int use_rock = 0;
66 int use_joliet = 0;
67 int do_listing = 0;
68 int do_find = 0;
69 int do_pathtab = 0;
70 char * xtract = 0;
71 int ucs_level = 0;
72
73 struct stat fstat_buf;
74 char name_buf[256];
75 char xname[256];
76 unsigned char date_buf[9];
77 unsigned int sector_offset = 0;
78
79 unsigned char buffer[2048];
80
81 #define PAGE sizeof(buffer)
82
83 #define ISODCL(from, to) (to - from + 1)
84
85
86 int
isonum_721(char * p)87 isonum_721 (char * p)
88 {
89 return ((p[0] & 0xff)
90 | ((p[1] & 0xff) << 8));
91 }
92
93
94 int
isonum_731(char * p)95 isonum_731 (char * p)
96 {
97 return ((p[0] & 0xff)
98 | ((p[1] & 0xff) << 8)
99 | ((p[2] & 0xff) << 16)
100 | ((p[3] & 0xff) << 24));
101 }
102
103
104 int
isonum_733(unsigned char * p)105 isonum_733 (unsigned char * p)
106 {
107 return (isonum_731 ((char *)p));
108 }
109
110
dump_pathtab(int block,int size)111 int dump_pathtab(int block, int size)
112 {
113 char * buffer;
114 int offset;
115 int index;
116 int extent;
117 int pindex;
118 int j;
119 int len;
120 char namebuf[255];
121
122 printf("Path table starts at block %d, size %d\n", block, size);
123
124 buffer = (char *) malloc(size);
125
126 lseek(fileno(infile), (block - sector_offset) << 11, 0);
127 read(fileno(infile), buffer, size);
128
129 offset = 0;
130 index = 1;
131 while(offset < size)
132 {
133 len = buffer[offset];
134 extent = isonum_731(buffer + offset + 2);
135 pindex = isonum_721(buffer + offset + 6);
136 switch( ucs_level )
137 {
138 case 3:
139 for(j=0; j < len; j++)
140 {
141 namebuf[j] = buffer[offset + 8 + j*2+1];
142 }
143 printf("%4d: %4d %x %s\n", index, pindex, extent, namebuf);
144 break;
145 case 0:
146 printf("%4d: %4d %x %s\n", index, pindex, extent, buffer + offset + 8);
147 }
148
149 index++;
150 offset += 8 + len;
151 if( offset & 1) offset++;
152 }
153
154 free(buffer);
155 }
156
parse_rr(unsigned char * pnt,int len,int cont_flag)157 int parse_rr(unsigned char * pnt, int len, int cont_flag)
158 {
159 int slen;
160 int ncount;
161 int extent;
162 int cont_extent, cont_offset, cont_size;
163 int flag1, flag2;
164 unsigned char *pnts;
165 char symlink[1024];
166 int goof;
167
168 symlink[0] = 0;
169
170 cont_extent = cont_offset = cont_size = 0;
171
172 ncount = 0;
173 flag1 = flag2 = 0;
174 while(len >= 4){
175 if(pnt[3] != 1) {
176 printf("**BAD RRVERSION");
177 return;
178 };
179 ncount++;
180 if(pnt[0] == 'R' && pnt[1] == 'R') flag1 = pnt[4] & 0xff;
181 if(strncmp(pnt, "PX", 2) == 0) flag2 |= 1;
182 if(strncmp(pnt, "PN", 2) == 0) flag2 |= 2;
183 if(strncmp(pnt, "SL", 2) == 0) flag2 |= 4;
184 if(strncmp(pnt, "NM", 2) == 0) flag2 |= 8;
185 if(strncmp(pnt, "CL", 2) == 0) flag2 |= 16;
186 if(strncmp(pnt, "PL", 2) == 0) flag2 |= 32;
187 if(strncmp(pnt, "RE", 2) == 0) flag2 |= 64;
188 if(strncmp(pnt, "TF", 2) == 0) flag2 |= 128;
189
190 if(strncmp(pnt, "PX", 2) == 0) {
191 fstat_buf.st_mode = isonum_733(pnt+4);
192 fstat_buf.st_nlink = isonum_733(pnt+12);
193 fstat_buf.st_uid = isonum_733(pnt+20);
194 fstat_buf.st_gid = isonum_733(pnt+28);
195 };
196
197 if(strncmp(pnt, "NM", 2) == 0) {
198 strncpy(name_buf, pnt+5, pnt[2] - 5);
199 name_buf[pnt[2] - 5] = 0;
200 }
201
202 if(strncmp(pnt, "CE", 2) == 0) {
203 cont_extent = isonum_733(pnt+4);
204 cont_offset = isonum_733(pnt+12);
205 cont_size = isonum_733(pnt+20);
206 };
207
208 if(strncmp(pnt, "PL", 2) == 0 || strncmp(pnt, "CL", 2) == 0) {
209 extent = isonum_733(pnt+4);
210 };
211
212 if(strncmp(pnt, "SL", 2) == 0) {
213 int cflag;
214
215 cflag = pnt[4];
216 pnts = pnt+5;
217 slen = pnt[2] - 5;
218 while(slen >= 1){
219 switch(pnts[0] & 0xfe){
220 case 0:
221 strncat(symlink, pnts+2, pnts[1]);
222 break;
223 case 2:
224 strcat (symlink, ".");
225 break;
226 case 4:
227 strcat (symlink, "..");
228 break;
229 case 8:
230 if((pnts[0] & 1) == 0)strcat (symlink, "/");
231 break;
232 case 16:
233 strcat(symlink,"/mnt");
234 printf("Warning - mount point requested");
235 break;
236 case 32:
237 strcat(symlink,"kafka");
238 printf("Warning - host_name requested");
239 break;
240 default:
241 printf("Reserved bit setting in symlink", goof++);
242 break;
243 };
244 if((pnts[0] & 0xfe) && pnts[1] != 0) {
245 printf("Incorrect length in symlink component");
246 };
247 if((pnts[0] & 1) == 0) strcat(symlink,"/");
248
249 slen -= (pnts[1] + 2);
250 pnts += (pnts[1] + 2);
251 if(xname[0] == 0) strcpy(xname, "-> ");
252 strcat(xname, symlink);
253 };
254 symlink[0] = 0;
255 };
256
257 len -= pnt[2];
258 pnt += pnt[2];
259 if(len <= 3 && cont_extent) {
260 unsigned char sector[2048];
261 lseek(fileno(infile), (cont_extent - sector_offset) << 11, 0);
262 read(fileno(infile), sector, sizeof(sector));
263 flag2 |= parse_rr(§or[cont_offset], cont_size, 1);
264 };
265 };
266 return flag2;
267 }
268
269 int
dump_rr(struct iso_directory_record * idr)270 dump_rr(struct iso_directory_record * idr)
271 {
272 int len;
273 unsigned char * pnt;
274
275 len = idr->length[0] & 0xff;
276 len -= sizeof(struct iso_directory_record);
277 len += sizeof(idr->name);
278 len -= idr->name_len[0];
279 pnt = (unsigned char *) idr;
280 pnt += sizeof(struct iso_directory_record);
281 pnt -= sizeof(idr->name);
282 pnt += idr->name_len[0];
283 if((idr->name_len[0] & 1) == 0){
284 pnt++;
285 len--;
286 };
287 parse_rr(pnt, len, 0);
288 }
289
290 struct todo
291 {
292 struct todo * next;
293 char * name;
294 int extent;
295 int length;
296 };
297
298 struct todo * todo_idr = NULL;
299
300 char * months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
301 "Aug", "Sep", "Oct", "Nov", "Dec"};
302
dump_stat(int extent)303 dump_stat(int extent)
304 {
305 int i;
306 char outline[80];
307
308 memset(outline, ' ', sizeof(outline));
309
310 if(S_ISREG(fstat_buf.st_mode))
311 outline[0] = '-';
312 else if(S_ISDIR(fstat_buf.st_mode))
313 outline[0] = 'd';
314 else if(S_ISLNK(fstat_buf.st_mode))
315 outline[0] = 'l';
316 else if(S_ISCHR(fstat_buf.st_mode))
317 outline[0] = 'c';
318 else if(S_ISBLK(fstat_buf.st_mode))
319 outline[0] = 'b';
320 else if(S_ISFIFO(fstat_buf.st_mode))
321 outline[0] = 'f';
322 else if(S_ISSOCK(fstat_buf.st_mode))
323 outline[0] = 's';
324 else
325 outline[0] = '?';
326
327 memset(outline+1, '-', 9);
328 if( fstat_buf.st_mode & S_IRUSR )
329 outline[1] = 'r';
330 if( fstat_buf.st_mode & S_IWUSR )
331 outline[2] = 'w';
332 if( fstat_buf.st_mode & S_IXUSR )
333 outline[3] = 'x';
334
335 if( fstat_buf.st_mode & S_IRGRP )
336 outline[4] = 'r';
337 if( fstat_buf.st_mode & S_IWGRP )
338 outline[5] = 'w';
339 if( fstat_buf.st_mode & S_IXGRP )
340 outline[6] = 'x';
341
342 if( fstat_buf.st_mode & S_IROTH )
343 outline[7] = 'r';
344 if( fstat_buf.st_mode & S_IWOTH )
345 outline[8] = 'w';
346 if( fstat_buf.st_mode & S_IXOTH )
347 outline[9] = 'x';
348
349 sprintf(outline+11, "%3d", fstat_buf.st_nlink);
350 sprintf(outline+15, "%4o", fstat_buf.st_uid);
351 sprintf(outline+20, "%4o", fstat_buf.st_gid);
352 sprintf(outline+33, "%8d", fstat_buf.st_size);
353
354 if( date_buf[1] >= 1 && date_buf[1] <= 12 )
355 {
356 memcpy(outline+42, months[date_buf[1]-1], 3);
357 }
358
359 sprintf(outline+46, "%2d", date_buf[2]);
360 sprintf(outline+49, "%4d", date_buf[0]+1900);
361
362 sprintf(outline+54, "[%6d]", extent);
363
364 for(i=0; i<63; i++)
365 if(outline[i] == 0) outline[i] = ' ';
366 outline[63] = 0;
367
368 printf("%s %s %s\n", outline, name_buf, xname);
369 }
370
extract_file(struct iso_directory_record * idr)371 extract_file(struct iso_directory_record * idr)
372 {
373 int extent, len, tlen;
374 unsigned char buff[2048];
375
376 extent = isonum_733((unsigned char *)idr->extent);
377 len = isonum_733((unsigned char *)idr->size);
378
379 while(len > 0)
380 {
381 lseek(fileno(infile), (extent - sector_offset) << 11, 0);
382 tlen = (len > sizeof(buff) ? sizeof(buff) : len);
383 read(fileno(infile), buff, tlen);
384 len -= tlen;
385 extent++;
386 write(1, buff, tlen);
387 }
388 }
389
parse_dir(char * rootname,int extent,int len)390 parse_dir(char * rootname, int extent, int len){
391 unsigned int k;
392 char testname[256];
393 struct todo * td;
394 int i, j;
395 struct iso_directory_record * idr;
396
397
398 if( do_listing)
399 printf("\nDirectory listing of %s\n", rootname);
400
401 while(len > 0 )
402 {
403 lseek(fileno(infile), (extent - sector_offset) << 11, 0);
404 read(fileno(infile), buffer, sizeof(buffer));
405 len -= sizeof(buffer);
406 extent++;
407 i = 0;
408 while(1==1){
409 idr = (struct iso_directory_record *) &buffer[i];
410 if(idr->length[0] == 0) break;
411 memset(&fstat_buf, 0, sizeof(fstat_buf));
412 name_buf[0] = xname[0] = 0;
413 fstat_buf.st_size = isonum_733((unsigned char *)idr->size);
414 if( idr->flags[0] & 2)
415 fstat_buf.st_mode |= S_IFDIR;
416 else
417 fstat_buf.st_mode |= S_IFREG;
418 if(idr->name_len[0] == 1 && idr->name[0] == 0)
419 strcpy(name_buf, ".");
420 else if(idr->name_len[0] == 1 && idr->name[0] == 1)
421 strcpy(name_buf, "..");
422 else {
423 switch(ucs_level)
424 {
425 case 3:
426 /*
427 * Unicode name. Convert as best we can.
428 */
429 {
430 int i;
431 unsigned short uni_name;
432
433 for(i=0; i < idr->name_len[0] / 2; i++)
434 {
435 name_buf[i] = idr->name[i*2+1];
436 }
437 name_buf[idr->name_len[0]/2] = '\0';
438 }
439 break;
440 case 0:
441 /*
442 * Normal non-Unicode name.
443 */
444 strncpy(name_buf, idr->name, idr->name_len[0]);
445 name_buf[idr->name_len[0]] = 0;
446 break;
447 default:
448 /*
449 * Don't know how to do these yet. Maybe they are the same
450 * as one of the above.
451 */
452 exit(1);
453 }
454 };
455 memcpy(date_buf, idr->date, 9);
456 if(use_rock) dump_rr(idr);
457 if( (idr->flags[0] & 2) != 0
458 && (idr->name_len[0] != 1
459 || (idr->name[0] != 0 && idr->name[0] != 1)))
460 {
461 /*
462 * Add this directory to the todo list.
463 */
464 td = todo_idr;
465 if( td != NULL )
466 {
467 while(td->next != NULL) td = td->next;
468 td->next = (struct todo *) malloc(sizeof(*td));
469 td = td->next;
470 }
471 else
472 {
473 todo_idr = td = (struct todo *) malloc(sizeof(*td));
474 }
475 td->next = NULL;
476 td->extent = isonum_733((unsigned char *)idr->extent);
477 td->length = isonum_733((unsigned char *)idr->size);
478 td->name = (char *) malloc(strlen(rootname)
479 + strlen(name_buf) + 2);
480 strcpy(td->name, rootname);
481 strcat(td->name, name_buf);
482 strcat(td->name, "/");
483 }
484 else
485 {
486 strcpy(testname, rootname);
487 strcat(testname, name_buf);
488 if(xtract && strcmp(xtract, testname) == 0)
489 {
490 extract_file(idr);
491 }
492 }
493 if( do_find
494 && (idr->name_len[0] != 1
495 || (idr->name[0] != 0 && idr->name[0] != 1)))
496 {
497 strcpy(testname, rootname);
498 strcat(testname, name_buf);
499 printf("%s\n", testname);
500 }
501 if(do_listing)
502 dump_stat(isonum_733((unsigned char *)idr->extent));
503 i += buffer[i];
504 if (i > 2048 - sizeof(struct iso_directory_record)) break;
505 }
506 }
507 }
508
usage()509 usage()
510 {
511 fprintf(stderr, "isoinfo -i filename [-l] [-R] [-x filename] [-f] [-N nsect]\n");
512 }
513
main(int argc,char * argv[])514 main(int argc, char * argv[]){
515 int c;
516 char buffer[2048];
517 int nbyte;
518 char * filename = NULL;
519 int i,j;
520 int toc_offset = 0;
521 struct todo * td;
522 struct iso_primary_descriptor ipd;
523 struct iso_directory_record * idr;
524
525 if(argc < 2) return 0;
526 while ((c = getopt(argc, argv, "pi:JRlx:fN:T:")) != EOF)
527 switch (c)
528 {
529 case 'f':
530 do_find++;
531 break;
532 case 'p':
533 do_pathtab++;
534 break;
535 case 'R':
536 use_rock++;
537 break;
538 case 'J':
539 use_joliet++;
540 break;
541 case 'l':
542 do_listing++;
543 break;
544 case 'T':
545 /*
546 * This is used if we have a complete multi-session
547 * disc that we want/need to play with.
548 * Here we specify the offset where we want to
549 * start searching for the TOC.
550 */
551 toc_offset = atol(optarg);
552 break;
553 case 'N':
554 /*
555 * Use this if we have an image of a single session
556 * and we need to list the directory contents.
557 * This is the session block number of the start
558 * of the session.
559 */
560 sector_offset = atol(optarg);
561 break;
562 case 'i':
563 filename = optarg;
564 break;
565 case 'x':
566 xtract = optarg;
567 break;
568 default:
569 usage();
570 exit(1);
571 }
572
573 if( filename == NULL )
574 {
575 fprintf(stderr, "Error - file not specified\n");
576 exit(1);
577 }
578
579 infile = fopen(filename,"rb");
580
581 if( infile == NULL )
582 {
583 fprintf(stderr,"Unable to open file %s\n", filename);
584 exit(1);
585 }
586
587 /*
588 * Absolute sector offset, so don't subtract sector_offset here.
589 */
590 lseek(fileno(infile), (16 + toc_offset) <<11, 0);
591 read(fileno(infile), &ipd, sizeof(ipd));
592
593 if( use_joliet )
594 {
595 int block = 16;
596 while( (unsigned char) ipd.type[0] != ISO_VD_END )
597 {
598 /*
599 * Find the UCS escape sequence.
600 */
601 if( ipd.escape_sequences[0] == '%'
602 && ipd.escape_sequences[1] == '/'
603 && ipd.escape_sequences[3] == '\0'
604 && ( ipd.escape_sequences[2] == '@'
605 || ipd.escape_sequences[2] == 'C'
606 || ipd.escape_sequences[2] == 'E') )
607 {
608 break;
609 }
610
611 block++;
612 lseek(fileno(infile), (block + toc_offset) <<11, 0);
613 read(fileno(infile), &ipd, sizeof(ipd));
614 }
615
616 if( (unsigned char) ipd.type[0] == ISO_VD_END )
617 {
618 fprintf(stderr, "Unable to find Joliet SVD\n");
619 exit(1);
620 }
621
622 switch(ipd.escape_sequences[2])
623 {
624 case '@':
625 ucs_level = 1;
626 break;
627 case 'C':
628 ucs_level = 2;
629 break;
630 case 'E':
631 ucs_level = 3;
632 break;
633 }
634
635 if( ucs_level < 3 )
636 {
637 fprintf(stderr, "Don't know what ucs_level == %d means\n", ucs_level);
638 exit(1);
639 }
640 }
641
642 idr = (struct iso_directory_record *) &ipd.root_directory_record;
643
644 if( do_pathtab )
645 {
646 dump_pathtab(isonum_731(ipd.type_l_path_table),
647 isonum_733((unsigned char *)ipd.path_table_size));
648 }
649
650 parse_dir("/", isonum_733((unsigned char *)idr->extent), isonum_733((unsigned char *)idr->size));
651 td = todo_idr;
652 while(td)
653 {
654 parse_dir(td->name, td->extent, td->length);
655 td = td->next;
656 }
657
658 fclose(infile);
659 }
660
661
662
663
664