1 /* 2 * Copyright 1999, Be Incorporated. All Rights Reserved. 3 * This file may be used under the terms of the Be Sample Code License. 4 * 5 * Copyright 2001, pinc Software. All Rights Reserved. 6 */ 7 8 9 #include "iso9660.h" 10 11 #include <ctype.h> 12 13 #ifndef FS_SHELL 14 # include <dirent.h> 15 # include <fcntl.h> 16 # include <stdlib.h> 17 # include <string.h> 18 # include <sys/stat.h> 19 # include <time.h> 20 # include <unistd.h> 21 22 # include <ByteOrder.h> 23 # include <Drivers.h> 24 # include <KernelExport.h> 25 # include <fs_cache.h> 26 #endif 27 28 #include "rock_ridge.h" 29 30 //#define TRACE_ISO9660 1 31 #if TRACE_ISO9660 32 # define TRACE(x) dprintf x 33 #else 34 # define TRACE(x) ; 35 #endif 36 37 38 // Just needed here 39 static status_t unicode_to_utf8(const char *src, int32 *srcLen, char *dst, 40 int32 *dstLen); 41 42 43 // ISO9660 should start with this string 44 const char *kISO9660IDString = "CD001"; 45 46 47 static int 48 get_device_block_size(int fd) 49 { 50 device_geometry geometry; 51 52 if (ioctl(fd, B_GET_GEOMETRY, &geometry) < 0) { 53 struct stat st; 54 if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode)) 55 return 0; 56 57 return 512; /* just assume it's a plain old file or something */ 58 } 59 60 return geometry.bytes_per_sector; 61 } 62 63 64 // From EncodingComversions.cpp 65 66 // Pierre's (modified) Uber Macro 67 68 // NOTE: iso9660 seems to store the unicode text in big-endian form 69 #define u_to_utf8(str, uni_str)\ 70 {\ 71 if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xff80) == 0)\ 72 *str++ = B_BENDIAN_TO_HOST_INT16(*uni_str++);\ 73 else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xf800) == 0) {\ 74 str[0] = 0xc0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6);\ 75 str[1] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\ 76 str += 2;\ 77 } else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xfc00) != 0xd800) {\ 78 str[0] = 0xe0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>12);\ 79 str[1] = 0x80|((B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6)&0x3f);\ 80 str[2] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\ 81 str += 3;\ 82 } else {\ 83 int val;\ 84 val = ((B_BENDIAN_TO_HOST_INT16(uni_str[0])-0xd7c0)<<10) | (B_BENDIAN_TO_HOST_INT16(uni_str[1])&0x3ff);\ 85 str[0] = 0xf0 | (val>>18);\ 86 str[1] = 0x80 | ((val>>12)&0x3f);\ 87 str[2] = 0x80 | ((val>>6)&0x3f);\ 88 str[3] = 0x80 | (val&0x3f);\ 89 uni_str += 2; str += 4;\ 90 }\ 91 } 92 93 94 static status_t 95 unicode_to_utf8(const char *src, int32 *srcLen, char *dst, int32 *dstLen) 96 { 97 int32 srcLimit = *srcLen; 98 int32 dstLimit = *dstLen; 99 int32 srcCount = 0; 100 int32 dstCount = 0; 101 102 for (srcCount = 0; srcCount < srcLimit; srcCount += 2) { 103 uint16 *UNICODE = (uint16 *)&src[srcCount]; 104 uint8 utf8[4]; 105 uint8 *UTF8 = utf8; 106 int32 utf8Len; 107 int32 j; 108 109 u_to_utf8(UTF8, UNICODE); 110 111 utf8Len = UTF8 - utf8; 112 if (dstCount + utf8Len > dstLimit) 113 break; 114 115 for (j = 0; j < utf8Len; j++) 116 dst[dstCount + j] = utf8[j]; 117 dstCount += utf8Len; 118 } 119 120 *srcLen = srcCount; 121 *dstLen = dstCount; 122 123 return dstCount > 0 ? B_NO_ERROR : B_ERROR; 124 } 125 126 127 static void 128 sanitize_iso_name(iso9660_inode* node, bool removeTrailingPoints) 129 { 130 // Get rid of semicolons, which are used to delineate file versions. 131 if (char* semi = strchr(node->name, ';')) { 132 semi[0] = '\0'; 133 node->name_length = semi - node->name; 134 } 135 136 if (removeTrailingPoints) { 137 // Get rid of trailing points 138 if (node->name_length > 2 && node->name[node->name_length - 1] == '.') 139 node->name[--node->name_length] = '\0'; 140 } 141 } 142 143 144 static int 145 init_volume_date(ISOVolDate *date, char *buffer) 146 { 147 memcpy(date, buffer, 17); 148 return 0; 149 } 150 151 152 static int 153 init_node_date(ISORecDate *date, char *buffer) 154 { 155 memcpy(date, buffer, 7); 156 return 0; 157 } 158 159 160 static status_t 161 InitVolDesc(iso9660_volume *volume, char *buffer) 162 { 163 TRACE(("InitVolDesc - ENTER\n")); 164 165 volume->volDescType = *(uint8 *)buffer++; 166 167 volume->stdIDString[5] = '\0'; 168 strncpy(volume->stdIDString, buffer, 5); 169 buffer += 5; 170 171 volume->volDescVersion = *(uint8 *)buffer; 172 buffer += 2; // 8th byte unused 173 174 volume->systemIDString[32] = '\0'; 175 strncpy(volume->systemIDString, buffer, 32); 176 buffer += 32; 177 TRACE(("InitVolDesc - system id string is %s\n", volume->systemIDString)); 178 179 volume->volIDString[32] = '\0'; 180 strncpy(volume->volIDString, buffer, 32); 181 buffer += (32 + 80-73 + 1); // bytes 80-73 unused 182 TRACE(("InitVolDesc - volume id string is %s\n", volume->volIDString)); 183 184 volume->volSpaceSize[LSB_DATA] = *(uint32 *)buffer; 185 buffer += 4; 186 volume->volSpaceSize[MSB_DATA] = *(uint32 *)buffer; 187 buffer+= (4 + 120-89 + 1); // bytes 120-89 unused 188 189 volume->volSetSize[LSB_DATA] = *(uint16*)buffer; 190 buffer += 2; 191 volume->volSetSize[MSB_DATA] = *(uint16*)buffer; 192 buffer += 2; 193 194 volume->volSeqNum[LSB_DATA] = *(uint16*)buffer; 195 buffer += 2; 196 volume->volSeqNum[MSB_DATA] = *(uint16*)buffer; 197 buffer += 2; 198 199 volume->logicalBlkSize[LSB_DATA] = *(uint16*)buffer; 200 buffer += 2; 201 volume->logicalBlkSize[MSB_DATA] = *(uint16*)buffer; 202 buffer += 2; 203 204 volume->pathTblSize[LSB_DATA] = *(uint32*)buffer; 205 buffer += 4; 206 volume->pathTblSize[MSB_DATA] = *(uint32*)buffer; 207 buffer += 4; 208 209 volume->lPathTblLoc[LSB_DATA] = *(uint16*)buffer; 210 buffer += 2; 211 volume->lPathTblLoc[MSB_DATA] = *(uint16*)buffer; 212 buffer += 2; 213 214 volume->optLPathTblLoc[LSB_DATA] = *(uint16*)buffer; 215 buffer += 2; 216 volume->optLPathTblLoc[MSB_DATA] = *(uint16*)buffer; 217 buffer += 2; 218 219 volume->mPathTblLoc[LSB_DATA] = *(uint16*)buffer; 220 buffer += 2; 221 volume->mPathTblLoc[MSB_DATA] = *(uint16*)buffer; 222 buffer += 2; 223 224 volume->optMPathTblLoc[LSB_DATA] = *(uint16*)buffer; 225 buffer += 2; 226 volume->optMPathTblLoc[MSB_DATA] = *(uint16*)buffer; 227 buffer += 2; 228 229 // Fill in directory record. 230 volume->joliet_level = 0; 231 InitNode(volume, &volume->rootDirRec, buffer, NULL); 232 233 volume->rootDirRec.id = ISO_ROOTNODE_ID; 234 buffer += 34; 235 236 volume->volSetIDString[128] = '\0'; 237 strncpy(volume->volSetIDString, buffer, 128); 238 buffer += 128; 239 TRACE(("InitVolDesc - volume set id string is %s\n", volume->volSetIDString)); 240 241 volume->pubIDString[128] = '\0'; 242 strncpy(volume->pubIDString, buffer, 128); 243 buffer += 128; 244 TRACE(("InitVolDesc - volume pub id string is %s\n", volume->pubIDString)); 245 246 volume->dataPreparer[128] = '\0'; 247 strncpy(volume->dataPreparer, buffer, 128); 248 buffer += 128; 249 TRACE(("InitVolDesc - volume dataPreparer string is %s\n", volume->dataPreparer)); 250 251 volume->appIDString[128] = '\0'; 252 strncpy(volume->appIDString, buffer, 128); 253 buffer += 128; 254 TRACE(("InitVolDesc - volume app id string is %s\n", volume->appIDString)); 255 256 volume->copyright[38] = '\0'; 257 strncpy(volume->copyright, buffer, 38); 258 buffer += 38; 259 TRACE(("InitVolDesc - copyright is %s\n", volume->copyright)); 260 261 volume->abstractFName[38] = '\0'; 262 strncpy(volume->abstractFName, buffer, 38); 263 buffer += 38; 264 265 volume->biblioFName[38] = '\0'; 266 strncpy(volume->biblioFName, buffer, 38); 267 buffer += 38; 268 269 init_volume_date(&volume->createDate, buffer); 270 buffer += 17; 271 272 init_volume_date(&volume->modDate, buffer); 273 buffer += 17; 274 275 init_volume_date(&volume->expireDate, buffer); 276 buffer += 17; 277 278 init_volume_date(&volume->effectiveDate, buffer); 279 buffer += 17; 280 281 volume->fileStructVers = *(uint8*)buffer; 282 return B_OK; 283 } 284 285 286 static status_t 287 parse_rock_ridge(iso9660_volume* volume, iso9660_inode* node, char* buffer, 288 char* end, bool relocated) 289 { 290 // Now we're at the start of the rock ridge stuff 291 char* altName = NULL; 292 char* slName = NULL; 293 uint16 altNameSize = 0; 294 uint16 slNameSize = 0; 295 uint8 slFlags = 0; 296 uint8 length = 0; 297 bool done = false; 298 299 TRACE(("RR: Start of extensions at %p\n", buffer)); 300 301 while (!done) { 302 buffer += length; 303 if (buffer + 2 >= end) 304 break; 305 length = *(uint8*)(buffer + 2); 306 if (buffer + length > end) 307 break; 308 if (length == 0) 309 break; 310 311 switch (((int)buffer[0] << 8) + buffer[1]) { 312 // Stat structure stuff 313 case 'PX': 314 { 315 uint8 bytePos = 3; 316 TRACE(("RR: found PX, length %u\n", length)); 317 node->attr.pxVer = *(uint8*)(buffer + bytePos++); 318 319 // st_mode 320 node->attr.stat[LSB_DATA].st_mode 321 = *(mode_t*)(buffer + bytePos); 322 bytePos += 4; 323 node->attr.stat[MSB_DATA].st_mode 324 = *(mode_t*)(buffer + bytePos); 325 bytePos += 4; 326 327 // st_nlink 328 node->attr.stat[LSB_DATA].st_nlink 329 = *(nlink_t*)(buffer+bytePos); 330 bytePos += 4; 331 node->attr.stat[MSB_DATA].st_nlink 332 = *(nlink_t*)(buffer + bytePos); 333 bytePos += 4; 334 335 // st_uid 336 node->attr.stat[LSB_DATA].st_uid 337 = *(uid_t*)(buffer + bytePos); 338 bytePos += 4; 339 node->attr.stat[MSB_DATA].st_uid 340 = *(uid_t*)(buffer + bytePos); 341 bytePos += 4; 342 343 // st_gid 344 node->attr.stat[LSB_DATA].st_gid 345 = *(gid_t*)(buffer + bytePos); 346 bytePos += 4; 347 node->attr.stat[MSB_DATA].st_gid 348 = *(gid_t*)(buffer + bytePos); 349 bytePos += 4; 350 break; 351 } 352 353 case 'PN': 354 TRACE(("RR: found PN, length %u\n", length)); 355 break; 356 357 // Symbolic link info 358 case 'SL': 359 { 360 uint8 bytePos = 3; 361 uint8 lastCompFlag = 0; 362 uint8 addPos = 0; 363 bool slDone = false; 364 bool useSeparator = true; 365 366 TRACE(("RR: found SL, length %u\n", length)); 367 TRACE(("Buffer is at %p\n", buffer)); 368 TRACE(("Current length is %u\n", slNameSize)); 369 //kernel_debugger(""); 370 node->attr.slVer = *(uint8*)(buffer + bytePos++); 371 slFlags = *(uint8*)(buffer + bytePos++); 372 373 TRACE(("sl flags are %u\n", slFlags)); 374 while (!slDone && bytePos < length) { 375 uint8 compFlag = *(uint8*)(buffer + bytePos++); 376 uint8 compLen = *(uint8*)(buffer + bytePos++); 377 378 if (slName == NULL) 379 useSeparator = false; 380 381 addPos = slNameSize; 382 383 TRACE(("sl comp flags are %u, length is %u\n", compFlag, compLen)); 384 TRACE(("Current name size is %u\n", slNameSize)); 385 386 switch (compFlag) { 387 case SLCP_CONTINUE: 388 useSeparator = false; 389 default: 390 // Add the component to the total path. 391 slNameSize += compLen; 392 if (useSeparator) 393 slNameSize++; 394 slName = (char*)realloc(slName, 395 slNameSize + 1); 396 if (slName == NULL) 397 return B_NO_MEMORY; 398 399 if (useSeparator) { 400 TRACE(("Adding separator\n")); 401 slName[addPos++] = '/'; 402 } 403 404 TRACE(("doing memcopy of %u bytes at offset %d\n", compLen, addPos)); 405 memcpy(slName + addPos, buffer + bytePos, 406 compLen); 407 408 addPos += compLen; 409 useSeparator = true; 410 break; 411 412 case SLCP_CURRENT: 413 TRACE(("InitNode - found link to current directory\n")); 414 slNameSize += 2; 415 slName = (char*)realloc(slName, 416 slNameSize + 1); 417 if (slName == NULL) 418 return B_NO_MEMORY; 419 420 memcpy(slName + addPos, "./", 2); 421 useSeparator = false; 422 break; 423 424 case SLCP_PARENT: 425 slNameSize += 3; 426 slName = (char*)realloc(slName, 427 slNameSize + 1); 428 if (slName == NULL) 429 return B_NO_MEMORY; 430 431 memcpy(slName + addPos, "../", 3); 432 useSeparator = false; 433 break; 434 435 case SLCP_ROOT: 436 TRACE(("InitNode - found link to root directory\n")); 437 slNameSize += 1; 438 slName = (char*)realloc(slName, 439 slNameSize + 1); 440 if (slName == NULL) 441 return B_NO_MEMORY; 442 memcpy(slName + addPos, "/", 1); 443 useSeparator = false; 444 break; 445 446 case SLCP_VOLROOT: 447 slDone = true; 448 break; 449 450 case SLCP_HOST: 451 slDone = true; 452 break; 453 } 454 if (slName != NULL) 455 slName[slNameSize] = '\0'; 456 lastCompFlag = compFlag; 457 bytePos += compLen; 458 TRACE(("Current sl name is \'%s\'\n", slName)); 459 } 460 node->attr.slName = slName; 461 TRACE(("InitNode = symlink name is \'%s\'\n", slName)); 462 break; 463 } 464 465 // Altername name 466 case 'NM': 467 { 468 uint8 bytePos = 3; 469 uint8 flags = 0; 470 uint16 oldEnd = altNameSize; 471 472 altNameSize += length - 5; 473 altName = (char*)realloc(altName, altNameSize + 1); 474 if (altName == NULL) 475 return B_NO_MEMORY; 476 477 TRACE(("RR: found NM, length %u\n", length)); 478 // Read flag and version. 479 node->attr.nmVer = *(uint8 *)(buffer + bytePos++); 480 flags = *(uint8 *)(buffer + bytePos++); 481 482 TRACE(("RR: nm buffer is %s, start at %p\n", (buffer + bytePos), buffer + bytePos)); 483 484 // Build the file name. 485 memcpy(altName + oldEnd, buffer + bytePos, length - 5); 486 altName[altNameSize] = '\0'; 487 TRACE(("RR: alt name is %s\n", altName)); 488 489 // If the name is not continued in another record, update 490 // the record name. 491 if (!(flags & NM_CONTINUE)) { 492 // Get rid of the ISO name, replace with RR name. 493 if (node->name != NULL) 494 free(node->name); 495 node->name = altName; 496 node->name_length = altNameSize; 497 } 498 break; 499 } 500 501 // Deep directory record masquerading as a file. 502 case 'CL': 503 { 504 TRACE(("RR: found CL, length %u\n", length)); 505 // Reinitialize the node with the information at the 506 // "." entry of the pointed to directory data 507 node->startLBN[LSB_DATA] = *(uint32*)(buffer+4); 508 node->startLBN[MSB_DATA] = *(uint32*)(buffer+8); 509 510 char* buffer = (char*)block_cache_get(volume->fBlockCache, 511 node->startLBN[FS_DATA_FORMAT]); 512 if (buffer == NULL) 513 break; 514 515 InitNode(volume, node, buffer, NULL, true); 516 block_cache_put(volume->fBlockCache, 517 node->startLBN[FS_DATA_FORMAT]); 518 break; 519 } 520 521 case 'PL': 522 TRACE(("RR: found PL, length %u\n", length)); 523 break; 524 525 case 'RE': 526 // Relocated directory, we should skip. 527 TRACE(("RR: found RE, length %u\n", length)); 528 if (!relocated) 529 return B_NOT_SUPPORTED; 530 break; 531 532 case 'TF': 533 TRACE(("RR: found TF, length %u\n", length)); 534 break; 535 536 case 'RR': 537 TRACE(("RR: found RR, length %u\n", length)); 538 break; 539 540 case 'SF': 541 TRACE(("RR: found SF, sparse files not supported!\n")); 542 // TODO: support sparse files 543 return B_NOT_SUPPORTED; 544 545 default: 546 if (buffer[0] == '\0') { 547 TRACE(("RR: end of extensions\n")); 548 done = true; 549 } else 550 TRACE(("RR: Unknown tag %c%c\n", buffer[0], buffer[1])); 551 break; 552 } 553 } 554 555 return B_OK; 556 } 557 558 // #pragma mark - ISO-9660 specific exported functions 559 560 561 status_t 562 ISOMount(const char *path, uint32 flags, iso9660_volume **_newVolume, 563 bool allowJoliet) 564 { 565 // path: path to device (eg, /dev/disk/scsi/030/raw) 566 // partition: partition number on device ???? 567 // flags: currently unused 568 569 // determine if it is an ISO volume. 570 char buffer[ISO_PVD_SIZE]; 571 bool done = false; 572 bool isISO = false; 573 off_t offset = 0x8000; 574 ssize_t retval; 575 partition_info partitionInfo; 576 int deviceBlockSize, multiplier; 577 iso9660_volume *volume; 578 579 (void)flags; 580 581 TRACE(("ISOMount - ENTER\n")); 582 583 volume = (iso9660_volume *)calloc(sizeof(iso9660_volume), 1); 584 if (volume == NULL) { 585 TRACE(("ISOMount - mem error \n")); 586 return B_NO_MEMORY; 587 } 588 589 memset(&partitionInfo, 0, sizeof(partition_info)); 590 591 /* open and lock the device */ 592 volume->fdOfSession = open(path, O_RDONLY); 593 594 /* try to open the raw device to get access to the other sessions as well */ 595 if (volume->fdOfSession >= 0) { 596 if (ioctl(volume->fdOfSession, B_GET_PARTITION_INFO, &partitionInfo) < 0) { 597 TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n")); 598 strcpy(partitionInfo.device, path); 599 } 600 TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device)); 601 602 volume->fd = open(partitionInfo.device, O_RDONLY); 603 } 604 605 if (volume->fdOfSession < 0 || volume->fd < 0) { 606 close(volume->fd); 607 close(volume->fdOfSession); 608 609 TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path)); 610 free(volume); 611 return B_BAD_VALUE; 612 } 613 614 deviceBlockSize = get_device_block_size(volume->fdOfSession); 615 if (deviceBlockSize < 0) { 616 TRACE(("ISO9660 ERROR - device block size is 0\n")); 617 close(volume->fd); 618 close(volume->fdOfSession); 619 620 free(volume); 621 return B_BAD_VALUE; 622 } 623 624 volume->joliet_level = 0; 625 while (!done && offset < 0x10000) { 626 retval = read_pos(volume->fdOfSession, offset, (void*)buffer, 627 ISO_PVD_SIZE); 628 if (retval < ISO_PVD_SIZE) { 629 isISO = false; 630 break; 631 } 632 633 if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) { 634 if (*buffer == 0x01 && !isISO) { 635 // ISO_VD_PRIMARY 636 off_t maxBlocks; 637 638 TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n")); 639 640 InitVolDesc(volume, buffer); 641 strncpy(volume->devicePath,path,127); 642 volume->id = ISO_ROOTNODE_ID; 643 TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT])); 644 645 multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT]; 646 TRACE(("ISOMount: block size multiplier is %d\n", multiplier)); 647 648 // if the session is on a real device, size != 0 649 if (partitionInfo.size != 0) { 650 maxBlocks = (partitionInfo.size + partitionInfo.offset) 651 / volume->logicalBlkSize[FS_DATA_FORMAT]; 652 } else 653 maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT]; 654 655 /* Initialize access to the cache so that we can do cached i/o */ 656 TRACE(("ISO9660: cache init: dev %d, max blocks %Ld\n", volume->fd, maxBlocks)); 657 volume->fBlockCache = block_cache_create(volume->fd, maxBlocks, 658 volume->logicalBlkSize[FS_DATA_FORMAT], true); 659 isISO = true; 660 } else if (*buffer == 0x02 && isISO && allowJoliet) { 661 // ISO_VD_SUPPLEMENTARY 662 663 // JOLIET extension 664 // test escape sequence for level of UCS-2 characterset 665 if (buffer[88] == 0x25 && buffer[89] == 0x2f) { 666 switch (buffer[90]) { 667 case 0x40: volume->joliet_level = 1; break; 668 case 0x43: volume->joliet_level = 2; break; 669 case 0x45: volume->joliet_level = 3; break; 670 } 671 672 TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level)); 673 674 // Because Joliet-stuff starts at other sector, 675 // update root directory record. 676 if (volume->joliet_level > 0) { 677 InitNode(volume, &volume->rootDirRec, &buffer[156], 678 NULL); 679 } 680 } 681 } else if (*(unsigned char *)buffer == 0xff) { 682 // ISO_VD_END 683 done = true; 684 } else 685 TRACE(("found header %d\n",*buffer)); 686 } 687 offset += 0x800; 688 } 689 690 if (!isISO) { 691 // It isn't an ISO disk. 692 close(volume->fdOfSession); 693 close(volume->fd); 694 free(volume); 695 696 TRACE(("ISOMount: Not an ISO9660 volume!\n")); 697 return B_BAD_VALUE; 698 } 699 700 TRACE(("ISOMount - EXIT, returning %p\n", volume)); 701 *_newVolume = volume; 702 return B_OK; 703 } 704 705 706 /*! Reads in a single directory entry and fills in the values in the 707 dirent struct. Uses the cookie to keep track of the current block 708 and position within the block. Also uses the cookie to determine when 709 it has reached the end of the directory file. 710 */ 711 status_t 712 ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent, 713 size_t bufferSize) 714 { 715 int result = B_NO_ERROR; 716 bool done = false; 717 718 TRACE(("ISOReadDirEnt - ENTER\n")); 719 720 while (!done) { 721 off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock) 722 * volume->logicalBlkSize[FS_DATA_FORMAT]; 723 724 // If we're at the end of the data in a block, move to the next block. 725 char *blockData; 726 while (true) { 727 blockData 728 = (char*)block_cache_get(volume->fBlockCache, cookie->block); 729 if (blockData != NULL && *(blockData + cookie->pos) == 0) { 730 // NULL data, move to next block. 731 block_cache_put(volume->fBlockCache, cookie->block); 732 blockData = NULL; 733 totalRead 734 += volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos; 735 cookie->pos = 0; 736 cookie->block++; 737 } else 738 break; 739 740 if (totalRead >= cookie->totalSize) 741 break; 742 } 743 744 off_t cacheBlock = cookie->block; 745 746 if (blockData != NULL && totalRead < cookie->totalSize) { 747 iso9660_inode node; 748 size_t bytesRead = 0; 749 result = InitNode(volume, &node, blockData + cookie->pos, 750 &bytesRead); 751 752 // if we hit an entry that we don't support, we just skip it 753 if (result != B_OK && result != B_NOT_SUPPORTED) 754 break; 755 756 if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) { 757 size_t nameBufferSize = bufferSize - sizeof(struct dirent); 758 759 dirent->d_dev = volume->id; 760 dirent->d_ino = ((ino_t)cookie->block << 30) 761 + (cookie->pos & 0x3fffffff); 762 dirent->d_reclen = sizeof(struct dirent) + node.name_length + 1; 763 764 if (node.name_length <= nameBufferSize) { 765 // need to do some size checking here. 766 strlcpy(dirent->d_name, node.name, node.name_length + 1); 767 TRACE(("ISOReadDirEnt - success, name is %s, block %Ld, " 768 "pos %Ld, inode id %Ld\n", dirent->d_name, cookie->block, 769 cookie->pos, dirent->d_ino)); 770 } else { 771 // TODO: this can be just normal if we support reading more 772 // than one entry. 773 TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in " 774 "buffer of size %d\n", node.name, (int)nameBufferSize)); 775 result = B_BAD_VALUE; 776 } 777 778 done = true; 779 } 780 781 cookie->pos += bytesRead; 782 783 if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) { 784 cookie->pos = 0; 785 cookie->block++; 786 } 787 } else { 788 if (totalRead >= cookie->totalSize) 789 result = B_ENTRY_NOT_FOUND; 790 else 791 result = B_NO_MEMORY; 792 done = true; 793 } 794 795 if (blockData != NULL) 796 block_cache_put(volume->fBlockCache, cacheBlock); 797 } 798 799 TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n", 800 strerror(result), dirent->d_ino)); 801 802 return result; 803 } 804 805 806 status_t 807 InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer, 808 size_t* _bytesRead, bool relocated) 809 { 810 uint8 recordLength = *(uint8*)buffer++; 811 size_t nameLength; 812 813 TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n", 814 buffer, recordLength)); 815 816 if (_bytesRead != NULL) 817 *_bytesRead = recordLength; 818 if (recordLength == 0) 819 return B_ENTRY_NOT_FOUND; 820 821 char* end = buffer + recordLength; 822 823 if (!relocated) { 824 node->cache = NULL; 825 node->name = NULL; 826 node->attr.slName = NULL; 827 memset(node->attr.stat, 0, sizeof(node->attr.stat)); 828 } else 829 free(node->attr.slName); 830 831 node->extAttrRecLen = *(uint8*)buffer++; 832 TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen)); 833 834 node->startLBN[LSB_DATA] = *(uint32*)buffer; 835 buffer += 4; 836 node->startLBN[MSB_DATA] = *(uint32*)buffer; 837 buffer += 4; 838 TRACE(("InitNode - data start LBN is %d\n", 839 (int)node->startLBN[FS_DATA_FORMAT])); 840 841 node->dataLen[LSB_DATA] = *(uint32*)buffer; 842 buffer += 4; 843 node->dataLen[MSB_DATA] = *(uint32*)buffer; 844 buffer += 4; 845 TRACE(("InitNode - data length is %d\n", 846 (int)node->dataLen[FS_DATA_FORMAT])); 847 848 init_node_date(&node->recordDate, buffer); 849 buffer += 7; 850 851 node->flags = *(uint8*)buffer; 852 buffer++; 853 TRACE(("InitNode - flags are %d\n", node->flags)); 854 855 node->fileUnitSize = *(uint8*)buffer; 856 buffer++; 857 TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize)); 858 859 node->interleaveGapSize = *(uint8*)buffer; 860 buffer++; 861 TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize)); 862 863 node->volSeqNum = *(uint32*)buffer; 864 buffer += 4; 865 TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum)); 866 867 nameLength = *(uint8*)buffer; 868 buffer++; 869 870 // for relocated directories we take the name from the placeholder entry 871 if (!relocated) { 872 node->name_length = nameLength; 873 TRACE(("InitNode - file id length is %u\n", node->name_length)); 874 } 875 876 // Set defaults, in case there is no RockRidge stuff. 877 node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0 878 ? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH 879 : S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 880 881 if (node->name_length == 0) { 882 TRACE(("InitNode - File ID String is 0 length\n")); 883 return B_ENTRY_NOT_FOUND; 884 } 885 886 if (!relocated) { 887 // JOLIET extension: 888 // on joliet discs, buffer[0] can be 0 for Unicoded filenames, 889 // so I've added a check here to test explicitely for 890 // directories (which have length 1) 891 // Take care of "." and "..", the first two dirents are 892 // these in iso. 893 if (node->name_length == 1 && buffer[0] == 0) { 894 node->name = strdup("."); 895 node->name_length = 1; 896 } else if (node->name_length == 1 && buffer[0] == 1) { 897 node->name = strdup(".."); 898 node->name_length = 2; 899 } else if (volume->joliet_level > 0) { 900 // JOLIET extension: convert Unicode16 string to UTF8 901 // Assume that the unicode->utf8 conversion produces 4 byte 902 // utf8 characters, and allocate that much space 903 node->name = (char*)malloc(node->name_length * 2 + 1); 904 if (node->name == NULL) 905 return B_NO_MEMORY; 906 907 int32 sourceLength = node->name_length; 908 int32 destLength = node->name_length * 2; 909 910 status_t status = unicode_to_utf8(buffer, &sourceLength, 911 node->name, &destLength); 912 if (status < B_OK) { 913 dprintf("iso9660: error converting unicode->utf8\n"); 914 return status; 915 } 916 917 node->name[destLength] = '\0'; 918 node->name_length = destLength; 919 920 sanitize_iso_name(node, false); 921 } else { 922 node->name = (char*)malloc(node->name_length + 1); 923 if (node->name == NULL) 924 return B_NO_MEMORY; 925 926 // convert all characters to lower case 927 for (uint32 i = 0; i < node->name_length; i++) 928 node->name[i] = tolower(buffer[i]); 929 930 node->name[node->name_length] = '\0'; 931 932 sanitize_iso_name(node, true); 933 } 934 935 if (node->name == NULL) { 936 TRACE(("InitNode - unable to allocate memory!\n")); 937 return B_NO_MEMORY; 938 } 939 } 940 941 buffer += nameLength; 942 if (nameLength % 2 == 0) 943 buffer++; 944 945 TRACE(("DirRec ID String is: %s\n", node->name)); 946 947 return parse_rock_ridge(volume, node, buffer, end, relocated); 948 } 949 950 951 status_t 952 ConvertRecDate(ISORecDate* inDate, time_t* outDate) 953 { 954 time_t time; 955 int days, i, year, tz; 956 957 year = inDate->year -70; 958 tz = inDate->offsetGMT; 959 960 if (year < 0) { 961 time = 0; 962 } else { 963 const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 964 965 days = (year * 365); 966 967 if (year > 2) 968 days += (year + 1)/ 4; 969 970 for (i = 1; (i < inDate->month) && (i < 12); i++) { 971 days += monlen[i-1]; 972 } 973 974 if (((year + 2) % 4) == 0 && inDate->month > 2) 975 days++; 976 977 days += inDate->date - 1; 978 time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60) 979 + inDate->second; 980 if (tz & 0x80) 981 tz |= (-1 << 8); 982 983 if (-48 <= tz && tz <= 52) 984 time += tz *15 * 60; 985 } 986 *outDate = time; 987 return 0; 988 } 989 990