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 slName[slNameSize] = '\0'; 455 lastCompFlag = compFlag; 456 bytePos += compLen; 457 TRACE(("Current sl name is \'%s\'\n", slName)); 458 } 459 node->attr.slName = slName; 460 TRACE(("InitNode = symlink name is \'%s\'\n", slName)); 461 break; 462 } 463 464 // Altername name 465 case 'NM': 466 { 467 uint8 bytePos = 3; 468 uint8 flags = 0; 469 uint16 oldEnd = altNameSize; 470 471 altNameSize += length - 5; 472 altName = (char*)realloc(altName, altNameSize + 1); 473 if (altName == NULL) 474 return B_NO_MEMORY; 475 476 TRACE(("RR: found NM, length %u\n", length)); 477 // Read flag and version. 478 node->attr.nmVer = *(uint8 *)(buffer + bytePos++); 479 flags = *(uint8 *)(buffer + bytePos++); 480 481 TRACE(("RR: nm buffer is %s, start at %p\n", (buffer + bytePos), buffer + bytePos)); 482 483 // Build the file name. 484 memcpy(altName + oldEnd, buffer + bytePos, length - 5); 485 altName[altNameSize] = '\0'; 486 TRACE(("RR: alt name is %s\n", altName)); 487 488 // If the name is not continued in another record, update 489 // the record name. 490 if (!(flags & NM_CONTINUE)) { 491 // Get rid of the ISO name, replace with RR name. 492 if (node->name != NULL) 493 free(node->name); 494 node->name = altName; 495 node->name_length = altNameSize; 496 } 497 break; 498 } 499 500 // Deep directory record masquerading as a file. 501 case 'CL': 502 { 503 TRACE(("RR: found CL, length %u\n", length)); 504 // Reinitialize the node with the information at the 505 // "." entry of the pointed to directory data 506 node->startLBN[LSB_DATA] = *(uint32*)(buffer+4); 507 node->startLBN[MSB_DATA] = *(uint32*)(buffer+8); 508 509 char* buffer = (char*)block_cache_get(volume->fBlockCache, 510 node->startLBN[FS_DATA_FORMAT]); 511 if (buffer == NULL) 512 break; 513 514 InitNode(volume, node, buffer, NULL, true); 515 block_cache_put(volume->fBlockCache, 516 node->startLBN[FS_DATA_FORMAT]); 517 break; 518 } 519 520 case 'PL': 521 TRACE(("RR: found PL, length %u\n", length)); 522 break; 523 524 case 'RE': 525 // Relocated directory, we should skip. 526 TRACE(("RR: found RE, length %u\n", length)); 527 if (!relocated) 528 return B_NOT_SUPPORTED; 529 break; 530 531 case 'TF': 532 TRACE(("RR: found TF, length %u\n", length)); 533 break; 534 535 case 'RR': 536 TRACE(("RR: found RR, length %u\n", length)); 537 break; 538 539 case 'SF': 540 TRACE(("RR: found SF, sparse files not supported!\n")); 541 // TODO: support sparse files 542 return B_NOT_SUPPORTED; 543 544 default: 545 if (buffer[0] == '\0') { 546 TRACE(("RR: end of extensions\n")); 547 done = true; 548 } else 549 TRACE(("RR: Unknown tag %c%c\n", buffer[0], buffer[1])); 550 break; 551 } 552 } 553 554 return B_OK; 555 } 556 557 // #pragma mark - ISO-9660 specific exported functions 558 559 560 status_t 561 ISOMount(const char *path, uint32 flags, iso9660_volume **_newVolume, 562 bool allowJoliet) 563 { 564 // path: path to device (eg, /dev/disk/scsi/030/raw) 565 // partition: partition number on device ???? 566 // flags: currently unused 567 568 // determine if it is an ISO volume. 569 char buffer[ISO_PVD_SIZE]; 570 bool done = false; 571 bool isISO = false; 572 off_t offset = 0x8000; 573 ssize_t retval; 574 partition_info partitionInfo; 575 int deviceBlockSize, multiplier; 576 iso9660_volume *volume; 577 578 (void)flags; 579 580 TRACE(("ISOMount - ENTER\n")); 581 582 volume = (iso9660_volume *)calloc(sizeof(iso9660_volume), 1); 583 if (volume == NULL) { 584 TRACE(("ISOMount - mem error \n")); 585 return B_NO_MEMORY; 586 } 587 588 memset(&partitionInfo, 0, sizeof(partition_info)); 589 590 /* open and lock the device */ 591 volume->fdOfSession = open(path, O_RDONLY); 592 593 /* try to open the raw device to get access to the other sessions as well */ 594 if (volume->fdOfSession >= 0) { 595 if (ioctl(volume->fdOfSession, B_GET_PARTITION_INFO, &partitionInfo) < 0) { 596 TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n")); 597 strcpy(partitionInfo.device, path); 598 } 599 TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device)); 600 601 volume->fd = open(partitionInfo.device, O_RDONLY); 602 } 603 604 if (volume->fdOfSession < 0 || volume->fd < 0) { 605 close(volume->fd); 606 close(volume->fdOfSession); 607 608 TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path)); 609 free(volume); 610 return B_BAD_VALUE; 611 } 612 613 deviceBlockSize = get_device_block_size(volume->fdOfSession); 614 if (deviceBlockSize < 0) { 615 TRACE(("ISO9660 ERROR - device block size is 0\n")); 616 close(volume->fd); 617 close(volume->fdOfSession); 618 619 free(volume); 620 return B_BAD_VALUE; 621 } 622 623 volume->joliet_level = 0; 624 while (!done && offset < 0x10000) { 625 retval = read_pos(volume->fdOfSession, offset, (void*)buffer, 626 ISO_PVD_SIZE); 627 if (retval < ISO_PVD_SIZE) { 628 isISO = false; 629 break; 630 } 631 632 if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) { 633 if (*buffer == 0x01 && !isISO) { 634 // ISO_VD_PRIMARY 635 off_t maxBlocks; 636 637 TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n")); 638 639 InitVolDesc(volume, buffer); 640 strncpy(volume->devicePath,path,127); 641 volume->id = ISO_ROOTNODE_ID; 642 TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT])); 643 644 multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT]; 645 TRACE(("ISOMount: block size multiplier is %d\n", multiplier)); 646 647 // if the session is on a real device, size != 0 648 if (partitionInfo.size != 0) { 649 maxBlocks = (partitionInfo.size + partitionInfo.offset) 650 / volume->logicalBlkSize[FS_DATA_FORMAT]; 651 } else 652 maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT]; 653 654 /* Initialize access to the cache so that we can do cached i/o */ 655 TRACE(("ISO9660: cache init: dev %d, max blocks %Ld\n", volume->fd, maxBlocks)); 656 volume->fBlockCache = block_cache_create(volume->fd, maxBlocks, 657 volume->logicalBlkSize[FS_DATA_FORMAT], true); 658 isISO = true; 659 } else if (*buffer == 0x02 && isISO && allowJoliet) { 660 // ISO_VD_SUPPLEMENTARY 661 662 // JOLIET extension 663 // test escape sequence for level of UCS-2 characterset 664 if (buffer[88] == 0x25 && buffer[89] == 0x2f) { 665 switch (buffer[90]) { 666 case 0x40: volume->joliet_level = 1; break; 667 case 0x43: volume->joliet_level = 2; break; 668 case 0x45: volume->joliet_level = 3; break; 669 } 670 671 TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level)); 672 673 // Because Joliet-stuff starts at other sector, 674 // update root directory record. 675 if (volume->joliet_level > 0) { 676 InitNode(volume, &volume->rootDirRec, &buffer[156], 677 NULL); 678 } 679 } 680 } else if (*(unsigned char *)buffer == 0xff) { 681 // ISO_VD_END 682 done = true; 683 } else 684 TRACE(("found header %d\n",*buffer)); 685 } 686 offset += 0x800; 687 } 688 689 if (!isISO) { 690 // It isn't an ISO disk. 691 close(volume->fdOfSession); 692 close(volume->fd); 693 free(volume); 694 695 TRACE(("ISOMount: Not an ISO9660 volume!\n")); 696 return B_BAD_VALUE; 697 } 698 699 TRACE(("ISOMount - EXIT, returning %p\n", volume)); 700 *_newVolume = volume; 701 return B_OK; 702 } 703 704 705 /*! Reads in a single directory entry and fills in the values in the 706 dirent struct. Uses the cookie to keep track of the current block 707 and position within the block. Also uses the cookie to determine when 708 it has reached the end of the directory file. 709 */ 710 status_t 711 ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent, 712 size_t bufferSize) 713 { 714 int result = B_NO_ERROR; 715 bool done = false; 716 717 TRACE(("ISOReadDirEnt - ENTER\n")); 718 719 while (!done) { 720 off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock) 721 * volume->logicalBlkSize[FS_DATA_FORMAT]; 722 723 // If we're at the end of the data in a block, move to the next block. 724 char *blockData; 725 while (true) { 726 blockData 727 = (char*)block_cache_get(volume->fBlockCache, cookie->block); 728 if (blockData != NULL && *(blockData + cookie->pos) == 0) { 729 // NULL data, move to next block. 730 block_cache_put(volume->fBlockCache, cookie->block); 731 blockData = NULL; 732 totalRead 733 += volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos; 734 cookie->pos = 0; 735 cookie->block++; 736 } else 737 break; 738 739 if (totalRead >= cookie->totalSize) 740 break; 741 } 742 743 off_t cacheBlock = cookie->block; 744 745 if (blockData != NULL && totalRead < cookie->totalSize) { 746 iso9660_inode node; 747 size_t bytesRead = 0; 748 result = InitNode(volume, &node, blockData + cookie->pos, 749 &bytesRead); 750 751 // if we hit an entry that we don't support, we just skip it 752 if (result != B_OK && result != B_NOT_SUPPORTED) 753 break; 754 755 if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) { 756 size_t nameBufferSize = bufferSize - sizeof(struct dirent); 757 758 dirent->d_dev = volume->id; 759 dirent->d_ino = ((ino_t)cookie->block << 30) 760 + (cookie->pos & 0x3fffffff); 761 dirent->d_reclen = sizeof(struct dirent) + node.name_length + 1; 762 763 if (node.name_length <= nameBufferSize) { 764 // need to do some size checking here. 765 strlcpy(dirent->d_name, node.name, node.name_length + 1); 766 TRACE(("ISOReadDirEnt - success, name is %s, block %Ld, " 767 "pos %Ld, inode id %Ld\n", dirent->d_name, cookie->block, 768 cookie->pos, dirent->d_ino)); 769 } else { 770 // TODO: this can be just normal if we support reading more 771 // than one entry. 772 TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in " 773 "buffer of size %d\n", node.name, (int)nameBufferSize)); 774 result = B_BAD_VALUE; 775 } 776 777 done = true; 778 } 779 780 cookie->pos += bytesRead; 781 782 if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) { 783 cookie->pos = 0; 784 cookie->block++; 785 } 786 } else { 787 if (totalRead >= cookie->totalSize) 788 result = B_ENTRY_NOT_FOUND; 789 else 790 result = B_NO_MEMORY; 791 done = true; 792 } 793 794 if (blockData != NULL) 795 block_cache_put(volume->fBlockCache, cacheBlock); 796 } 797 798 TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n", 799 strerror(result), dirent->d_ino)); 800 801 return result; 802 } 803 804 805 status_t 806 InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer, 807 size_t* _bytesRead, bool relocated) 808 { 809 uint8 recordLength = *(uint8*)buffer++; 810 size_t nameLength; 811 812 TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n", 813 buffer, recordLength)); 814 815 if (_bytesRead != NULL) 816 *_bytesRead = recordLength; 817 if (recordLength == 0) 818 return B_ENTRY_NOT_FOUND; 819 820 char* end = buffer + recordLength; 821 822 if (!relocated) { 823 node->cache = NULL; 824 node->name = NULL; 825 node->attr.slName = NULL; 826 memset(node->attr.stat, 0, sizeof(node->attr.stat)); 827 } else 828 free(node->attr.slName); 829 830 node->extAttrRecLen = *(uint8*)buffer++; 831 TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen)); 832 833 node->startLBN[LSB_DATA] = *(uint32*)buffer; 834 buffer += 4; 835 node->startLBN[MSB_DATA] = *(uint32*)buffer; 836 buffer += 4; 837 TRACE(("InitNode - data start LBN is %d\n", 838 (int)node->startLBN[FS_DATA_FORMAT])); 839 840 node->dataLen[LSB_DATA] = *(uint32*)buffer; 841 buffer += 4; 842 node->dataLen[MSB_DATA] = *(uint32*)buffer; 843 buffer += 4; 844 TRACE(("InitNode - data length is %d\n", 845 (int)node->dataLen[FS_DATA_FORMAT])); 846 847 init_node_date(&node->recordDate, buffer); 848 buffer += 7; 849 850 node->flags = *(uint8*)buffer; 851 buffer++; 852 TRACE(("InitNode - flags are %d\n", node->flags)); 853 854 node->fileUnitSize = *(uint8*)buffer; 855 buffer++; 856 TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize)); 857 858 node->interleaveGapSize = *(uint8*)buffer; 859 buffer++; 860 TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize)); 861 862 node->volSeqNum = *(uint32*)buffer; 863 buffer += 4; 864 TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum)); 865 866 nameLength = *(uint8*)buffer; 867 buffer++; 868 869 // for relocated directories we take the name from the placeholder entry 870 if (!relocated) { 871 node->name_length = nameLength; 872 TRACE(("InitNode - file id length is %u\n", node->name_length)); 873 } 874 875 // Set defaults, in case there is no RockRidge stuff. 876 node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0 877 ? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH 878 : S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 879 880 if (node->name_length == 0) { 881 TRACE(("InitNode - File ID String is 0 length\n")); 882 return B_ENTRY_NOT_FOUND; 883 } 884 885 if (!relocated) { 886 // JOLIET extension: 887 // on joliet discs, buffer[0] can be 0 for Unicoded filenames, 888 // so I've added a check here to test explicitely for 889 // directories (which have length 1) 890 // Take care of "." and "..", the first two dirents are 891 // these in iso. 892 if (node->name_length == 1 && buffer[0] == 0) { 893 node->name = strdup("."); 894 node->name_length = 1; 895 } else if (node->name_length == 1 && buffer[0] == 1) { 896 node->name = strdup(".."); 897 node->name_length = 2; 898 } else if (volume->joliet_level > 0) { 899 // JOLIET extension: convert Unicode16 string to UTF8 900 // Assume that the unicode->utf8 conversion produces 4 byte 901 // utf8 characters, and allocate that much space 902 node->name = (char*)malloc(node->name_length * 2 + 1); 903 if (node->name == NULL) 904 return B_NO_MEMORY; 905 906 int32 sourceLength = node->name_length; 907 int32 destLength = node->name_length * 2; 908 909 status_t status = unicode_to_utf8(buffer, &sourceLength, 910 node->name, &destLength); 911 if (status < B_OK) { 912 dprintf("iso9660: error converting unicode->utf8\n"); 913 return status; 914 } 915 916 node->name[destLength] = '\0'; 917 node->name_length = destLength; 918 919 sanitize_iso_name(node, false); 920 } else { 921 node->name = (char*)malloc(node->name_length + 1); 922 if (node->name == NULL) 923 return B_NO_MEMORY; 924 925 // convert all characters to lower case 926 for (uint32 i = 0; i < node->name_length; i++) 927 node->name[i] = tolower(buffer[i]); 928 929 node->name[node->name_length] = '\0'; 930 931 sanitize_iso_name(node, true); 932 } 933 934 if (node->name == NULL) { 935 TRACE(("InitNode - unable to allocate memory!\n")); 936 return B_NO_MEMORY; 937 } 938 } 939 940 buffer += nameLength; 941 if (nameLength % 2 == 0) 942 buffer++; 943 944 TRACE(("DirRec ID String is: %s\n", node->name)); 945 946 return parse_rock_ridge(volume, node, buffer, end, relocated); 947 } 948 949 950 status_t 951 ConvertRecDate(ISORecDate* inDate, time_t* outDate) 952 { 953 time_t time; 954 int days, i, year, tz; 955 956 year = inDate->year -70; 957 tz = inDate->offsetGMT; 958 959 if (year < 0) { 960 time = 0; 961 } else { 962 const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 963 964 days = (year * 365); 965 966 if (year > 2) 967 days += (year + 1)/ 4; 968 969 for (i = 1; (i < inDate->month) && (i < 12); i++) { 970 days += monlen[i-1]; 971 } 972 973 if (((year + 2) % 4) == 0 && inDate->month > 2) 974 days++; 975 976 days += inDate->date - 1; 977 time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60) 978 + inDate->second; 979 if (tz & 0x80) 980 tz |= (-1 << 8); 981 982 if (-48 <= tz && tz <= 52) 983 time += tz *15 * 60; 984 } 985 *outDate = time; 986 return 0; 987 } 988 989