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(§or[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