xref: /openbsd/gnu/usr.sbin/mkhybrid/src/diag/isoinfo.c (revision 771fbea0)
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
87 isonum_721 (char * p)
88 {
89 	return ((p[0] & 0xff)
90 		| ((p[1] & 0xff) << 8));
91 }
92 
93 
94 int
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
105 isonum_733 (unsigned char * p)
106 {
107 	return (isonum_731 ((char *)p));
108 }
109 
110 
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 
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(&sector[cont_offset], cont_size, 1);
264 		};
265 	};
266 	return flag2;
267 }
268 
269 int
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 
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 
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 
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 
509 usage()
510 {
511   fprintf(stderr, "isoinfo -i filename [-l] [-R] [-x filename] [-f] [-N nsect]\n");
512 }
513 
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