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