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