1 /* 2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "Recognition.h" 9 10 #include "UdfString.h" 11 #include "MemoryChunk.h" 12 #include "Utils.h" 13 14 #include <string.h> 15 16 17 //------------------------------------------------------------------------------ 18 // forward declarations 19 //------------------------------------------------------------------------------ 20 21 static status_t 22 walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, 23 uint32 blockShift); 24 25 static status_t 26 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, 27 uint32 blockSize, uint32 blockShift, 28 primary_volume_descriptor &primaryVolumeDescriptor, 29 logical_volume_descriptor &logicalVolumeDescriptor, 30 partition_descriptor partitionDescriptors[], 31 uint8 &partitionDescriptorCount); 32 33 static status_t 34 walk_volume_descriptor_sequence(extent_address descriptorSequence, int device, 35 uint32 blockSize, uint32 blockShift, 36 primary_volume_descriptor &primaryVolumeDescriptor, 37 logical_volume_descriptor &logicalVolumeDescriptor, 38 partition_descriptor partitionDescriptors[], 39 uint8 &partitionDescriptorCount); 40 41 static status_t 42 walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift, 43 extent_address descriptorSequence, uint32 sequenceNumber = 0); 44 45 //------------------------------------------------------------------------------ 46 // externally visible functions 47 //------------------------------------------------------------------------------ 48 49 50 status_t 51 udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, 52 uint32 &blockShift, primary_volume_descriptor &primaryVolumeDescriptor, 53 logical_volume_descriptor &logicalVolumeDescriptor, 54 partition_descriptor partitionDescriptors[], 55 uint8 &partitionDescriptorCount) 56 { 57 TRACE(("udf_recognize: device: = %d, offset = %" B_PRIdOFF ", length = %" 58 B_PRIdOFF ", blockSize = %" B_PRIu32 "\n", device, offset, length, 59 blockSize)); 60 // Check the block size 61 status_t status = get_block_shift(blockSize, blockShift); 62 if (status != B_OK) { 63 TRACE_ERROR(("\nudf_recognize: Block size must be a positive power of " 64 "two! (blockSize = %" B_PRIu32 ")\n", blockSize)); 65 return status; 66 } 67 TRACE(("blockShift: %" B_PRIu32 "\n", blockShift)); 68 69 // Check for a valid volume recognition sequence 70 status = walk_volume_recognition_sequence(device, offset, blockSize, 71 blockShift); 72 if (status != B_OK) { 73 TRACE(("udf_recognize: Invalid sequence. status = %" B_PRId32 74 "\n", status)); 75 return status; 76 } 77 // Now hunt down a volume descriptor sequence from one of 78 // the anchor volume pointers (if there are any). 79 status = walk_anchor_volume_descriptor_sequences(device, offset, length, 80 blockSize, blockShift, primaryVolumeDescriptor, 81 logicalVolumeDescriptor, partitionDescriptors, 82 partitionDescriptorCount); 83 if (status != B_OK) { 84 TRACE_ERROR(("udf_recognize: cannot find volume descriptor. status = %" 85 B_PRId32 "\n", status)); 86 return status; 87 } 88 // Now walk the integrity sequence and make sure the last integrity 89 // descriptor is a closed descriptor 90 status = walk_integrity_sequence(device, blockSize, blockShift, 91 logicalVolumeDescriptor.integrity_sequence_extent()); 92 if (status != B_OK) { 93 TRACE_ERROR(("udf_recognize: last integrity descriptor not closed. " 94 "status = %" B_PRId32 "\n", status)); 95 return status; 96 } 97 98 return B_OK; 99 } 100 101 //------------------------------------------------------------------------------ 102 // local functions 103 //------------------------------------------------------------------------------ 104 105 static 106 status_t 107 walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, 108 uint32 blockShift) 109 { 110 TRACE(("walk_volume_recognition_sequence: device = %d, offset = %" 111 B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n", 112 device, offset, blockSize, blockShift)); 113 114 // vrs starts at block 16. Each volume structure descriptor (vsd) 115 // should be one block long. We're expecting to find 0 or more iso9660 116 // vsd's followed by some ECMA-167 vsd's. 117 MemoryChunk chunk(blockSize); 118 if (chunk.InitCheck() != B_OK) { 119 TRACE_ERROR(("walk_volume_recognition_sequence: Failed to construct " 120 "MemoryChunk\n")); 121 return chunk.InitCheck(); 122 } 123 124 bool foundExtended = false; 125 bool foundECMA167 = false; 126 bool foundECMA168 = false; 127 for (uint32 block = 16; true; block++) { 128 off_t address = (offset + block) << blockShift; 129 TRACE(("walk_volume_recognition_sequence: block = %" B_PRIu32 ", " 130 "address = %" B_PRIdOFF ", ", block, address)); 131 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 132 if (bytesRead == (ssize_t)blockSize) { 133 volume_structure_descriptor_header* descriptor 134 = (volume_structure_descriptor_header *)(chunk.Data()); 135 if (descriptor->id_matches(kVSDID_ISO)) { 136 TRACE(("found ISO9660 descriptor\n")); 137 } else if (descriptor->id_matches(kVSDID_BEA)) { 138 TRACE(("found BEA descriptor\n")); 139 foundExtended = true; 140 } else if (descriptor->id_matches(kVSDID_TEA)) { 141 TRACE(("found TEA descriptor\n")); 142 foundExtended = true; 143 } else if (descriptor->id_matches(kVSDID_ECMA167_2)) { 144 TRACE(("found ECMA-167 rev 2 descriptor\n")); 145 foundECMA167 = true; 146 } else if (descriptor->id_matches(kVSDID_ECMA167_3)) { 147 TRACE(("found ECMA-167 rev 3 descriptor\n")); 148 foundECMA167 = true; 149 } else if (descriptor->id_matches(kVSDID_BOOT)) { 150 TRACE(("found boot descriptor\n")); 151 } else if (descriptor->id_matches(kVSDID_ECMA168)) { 152 TRACE(("found ECMA-168 descriptor\n")); 153 foundECMA168 = true; 154 } else { 155 TRACE(("found invalid descriptor, id = `%.5s'\n", descriptor->id)); 156 break; 157 } 158 } else { 159 TRACE(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32 ") " 160 "failed with: 0x%lx\n", address, blockSize, bytesRead)); 161 break; 162 } 163 } 164 165 // If we find an ECMA-167 descriptor, OR if we find a beginning 166 // or terminating extended area descriptor with NO ECMA-168 167 // descriptors, we return B_OK to signal that we should go 168 // looking for valid anchors. 169 return foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR; 170 } 171 172 173 static status_t 174 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, 175 uint32 blockSize, uint32 blockShift, 176 primary_volume_descriptor &primaryVolumeDescriptor, 177 logical_volume_descriptor &logicalVolumeDescriptor, 178 partition_descriptor partitionDescriptors[], 179 uint8 &partitionDescriptorCount) 180 { 181 DEBUG_INIT(NULL); 182 const uint8 avds_location_count = 4; 183 const off_t avds_locations[avds_location_count] 184 = { 256, length-1-256, length-1, 512, }; 185 bool found_vds = false; 186 for (int32 i = 0; i < avds_location_count; i++) { 187 off_t block = avds_locations[i]; 188 off_t address = (offset + block) << blockShift; 189 MemoryChunk chunk(blockSize); 190 anchor_volume_descriptor *anchor = NULL; 191 192 status_t anchorErr = chunk.InitCheck(); 193 if (!anchorErr) { 194 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 195 anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; 196 if (anchorErr) { 197 PRINT(("block %" B_PRIdOFF ": read_pos(pos:%" B_PRIdOFF ", " 198 "len:%" PRIu32 ") failed with error 0x%lx\n", 199 block, address, blockSize, bytesRead)); 200 } 201 } 202 if (!anchorErr) { 203 anchor = reinterpret_cast<anchor_volume_descriptor*>(chunk.Data()); 204 anchorErr = anchor->tag().init_check(block + offset); 205 if (anchorErr) { 206 PRINT(("block %" B_PRIdOFF ": invalid anchor\n", block)); 207 } else { 208 PRINT(("block %" B_PRIdOFF ": valid anchor\n", block)); 209 } 210 } 211 if (!anchorErr) { 212 PRINT(("block %" B_PRIdOFF ": anchor:\n", block)); 213 PDUMP(anchor); 214 // Found an avds, so try the main sequence first, then 215 // the reserve sequence if the main one fails. 216 anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), 217 device, blockSize, blockShift, primaryVolumeDescriptor, 218 logicalVolumeDescriptor, partitionDescriptors, 219 partitionDescriptorCount); 220 221 if (anchorErr) 222 anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), 223 device, blockSize, blockShift, primaryVolumeDescriptor, 224 logicalVolumeDescriptor, partitionDescriptors, 225 partitionDescriptorCount); 226 } 227 if (!anchorErr) { 228 PRINT(("block %" B_PRIdOFF ": found valid vds\n", 229 avds_locations[i])); 230 found_vds = true; 231 break; 232 } else { 233 // Both failed, so loop around and try another avds 234 PRINT(("block %" B_PRIdOFF ": vds search failed\n", 235 avds_locations[i])); 236 } 237 } 238 status_t error = found_vds ? B_OK : B_ERROR; 239 RETURN(error); 240 } 241 242 static status_t 243 walk_tagid_partition_descriptor(descriptor_tag *tag, off_t block, 244 uint8& uniquePartitions, partition_descriptor* partitionDescriptors) 245 { 246 DEBUG_INIT(NULL); 247 248 status_t error = B_OK; 249 partition_descriptor *partition = 250 reinterpret_cast<partition_descriptor*>(tag); 251 PDUMP(partition); 252 if (partition->tag().init_check(block) == B_OK) { 253 // Check for a previously discovered partition descriptor with 254 // the same number as this partition. If found, keep the one with 255 // the higher vds number. 256 bool foundDuplicate = false; 257 int num; 258 for (num = 0; num < uniquePartitions; num++) { 259 if (partitionDescriptors[num].partition_number() 260 == partition->partition_number()) { 261 foundDuplicate = true; 262 if (partitionDescriptors[num].vds_number() 263 < partition->vds_number()) { 264 partitionDescriptors[num] = *partition; 265 PRINT(("Replacing previous partition #%d " 266 "(vds_number: %" B_PRIu32 ") with new partition #%d " 267 "(vds_number: %" B_PRIu32 ")\n", 268 partitionDescriptors[num].partition_number(), 269 partitionDescriptors[num].vds_number(), 270 partition->partition_number(), 271 partition->vds_number())); 272 } 273 break; 274 } 275 } 276 // If we didn't find a duplicate, see if we have any open descriptor 277 // spaces left. 278 if (!foundDuplicate) { 279 if (num < kMaxPartitionDescriptors) { 280 // At least one more partition descriptor allowed 281 partitionDescriptors[num] = *partition; 282 uniquePartitions++; 283 PRINT(("Adding partition #%d (vds_number: %" B_PRIu32 ")\n", 284 partition->partition_number(), 285 partition->vds_number())); 286 } else { 287 // We've found more than kMaxPartitionDescriptor uniquely- 288 // numbered partitions. So, search through the partitions 289 // we already have again, this time just looking for a 290 // partition with a lower vds number. If we find one, 291 // replace it with this one. If we don't, scream bloody 292 // murder. 293 bool foundReplacement = false; 294 for (int j = 0; j < uniquePartitions; j++) { 295 if (partitionDescriptors[j].vds_number() 296 < partition->vds_number()) { 297 foundReplacement = true; 298 partitionDescriptors[j] = *partition; 299 PRINT(("Replacing partition #%d " 300 "(vds_number: %" B_PRIu32 ") " 301 "with partition #%d " 302 "(vds_number: %" B_PRIu32 ")\n", 303 partitionDescriptors[j].partition_number(), 304 partitionDescriptors[j].vds_number(), 305 partition->partition_number(), 306 partition->vds_number())); 307 break; 308 } 309 } 310 if (!foundReplacement) { 311 PRINT(("Found more than kMaxPartitionDescriptors == %d " 312 "unique partition descriptors!\n", 313 kMaxPartitionDescriptors)); 314 error = B_BAD_VALUE; 315 } 316 } 317 } 318 } 319 320 RETURN(error); 321 } 322 323 static 324 status_t 325 walk_volume_descriptor_sequence(extent_address descriptorSequence, 326 int device, uint32 blockSize, uint32 blockShift, 327 primary_volume_descriptor &primaryVolumeDescriptor, 328 logical_volume_descriptor &logicalVolumeDescriptor, 329 partition_descriptor partitionDescriptors[], 330 uint8 &partitionDescriptorCount) 331 { 332 DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%" PRIu32 ", " 333 "descriptorSequence.len:%" PRIu32, 334 descriptorSequence.location(), descriptorSequence.length())); 335 uint32 count = descriptorSequence.length() >> blockShift; 336 337 bool foundLogicalVolumeDescriptor = false; 338 bool foundUnallocatedSpaceDescriptor = false; 339 bool foundUdfImplementationUseDescriptor = false; 340 uint8 uniquePartitions = 0; 341 status_t error = B_OK; 342 343 for (uint32 i = 0; i < count; i++) { 344 off_t block = descriptorSequence.location()+i; 345 off_t address = block << blockShift; 346 MemoryChunk chunk(blockSize); 347 descriptor_tag *tag = NULL; 348 349 PRINT(("descriptor #%" PRIu32 " (block %" B_PRIdOFF "):\n", i, block)); 350 351 status_t loopError = chunk.InitCheck(); 352 if (!loopError) { 353 ssize_t bytesRead = read_pos(device, address, chunk.Data(), 354 blockSize); 355 loopError = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; 356 if (loopError) { 357 PRINT(("block %" B_PRIdOFF ": read_pos(pos:%" B_PRIdOFF ", " 358 "len:%" B_PRIu32 ") failed with error 0x%lx\n", 359 block, address, blockSize, bytesRead)); 360 } 361 } 362 if (!loopError) { 363 tag = reinterpret_cast<descriptor_tag *>(chunk.Data()); 364 loopError = tag->init_check(block); 365 } 366 if (!loopError) { 367 // Now decide what type of descriptor we have 368 switch (tag->id()) { 369 case TAGID_UNDEFINED: 370 break; 371 372 case TAGID_PRIMARY_VOLUME_DESCRIPTOR: 373 { 374 primary_volume_descriptor *primary = 375 reinterpret_cast<primary_volume_descriptor*>(tag); 376 PDUMP(primary); 377 primaryVolumeDescriptor = *primary; 378 break; 379 } 380 381 case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: 382 break; 383 384 case TAGID_VOLUME_DESCRIPTOR_POINTER: 385 break; 386 387 case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: 388 { 389 implementation_use_descriptor *impUse = 390 reinterpret_cast<implementation_use_descriptor*>(tag); 391 PDUMP(impUse); 392 // Check for a matching implementation id string 393 // (note that the revision version is not checked) 394 if (impUse->tag().init_check(block) == B_OK 395 && impUse->implementation_id().matches( 396 kLogicalVolumeInfoId201)) { 397 foundUdfImplementationUseDescriptor = true; 398 } 399 break; 400 } 401 402 case TAGID_PARTITION_DESCRIPTOR: 403 { 404 error = walk_tagid_partition_descriptor( 405 tag, block, uniquePartitions, partitionDescriptors); 406 break; 407 } 408 409 case TAGID_LOGICAL_VOLUME_DESCRIPTOR: 410 { 411 logical_volume_descriptor *logical = 412 reinterpret_cast<logical_volume_descriptor*>(tag); 413 PDUMP(logical); 414 if (foundLogicalVolumeDescriptor) { 415 // Keep the vd with the highest vds_number 416 if (logicalVolumeDescriptor.vds_number() 417 < logical->vds_number()) { 418 logicalVolumeDescriptor = *logical; 419 } 420 } else { 421 logicalVolumeDescriptor = *logical; 422 foundLogicalVolumeDescriptor = true; 423 } 424 break; 425 } 426 427 case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: 428 { 429 unallocated_space_descriptor *unallocated = 430 reinterpret_cast<unallocated_space_descriptor*>(tag); 431 PDUMP(unallocated); 432 foundUnallocatedSpaceDescriptor = true; 433 (void)unallocated; // kill the warning 434 break; 435 } 436 437 case TAGID_TERMINATING_DESCRIPTOR: 438 { 439 terminating_descriptor *terminating = 440 reinterpret_cast<terminating_descriptor*>(tag); 441 PDUMP(terminating); 442 (void)terminating; // kill the warning 443 break; 444 } 445 446 case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR: 447 // Not found in this descriptor sequence 448 break; 449 450 default: 451 break; 452 453 } 454 } 455 } 456 457 PRINT(("found %d unique partition%s\n", uniquePartitions, 458 (uniquePartitions == 1 ? "" : "s"))); 459 460 if (!error && !foundUdfImplementationUseDescriptor) { 461 INFORM(("WARNING: no valid udf implementation use descriptor found\n")); 462 } 463 if (!error) 464 error = foundLogicalVolumeDescriptor 465 && foundUnallocatedSpaceDescriptor 466 ? B_OK : B_ERROR; 467 if (!error) 468 error = uniquePartitions >= 1 ? B_OK : B_ERROR; 469 if (!error) 470 partitionDescriptorCount = uniquePartitions; 471 472 RETURN(error); 473 } 474 475 /*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence. 476 477 \return 478 - \c B_OK: Success. the sequence was terminated by a valid, closed 479 integrity descriptor. 480 - \c B_ENTRY_NOT_FOUND: The sequence was empty. 481 - (other error code): The sequence was non-empty and did not end in a valid, 482 closed integrity descriptor. 483 */ 484 static status_t 485 walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift, 486 extent_address descriptorSequence, uint32 sequenceNumber) 487 { 488 DEBUG_INIT_ETC(NULL, 489 ("descriptorSequence.loc:%" B_PRIu32 ", " 490 "descriptorSequence.len:%" B_PRIu32 , 491 descriptorSequence.location(), descriptorSequence.length())); 492 uint32 count = descriptorSequence.length() >> blockShift; 493 494 bool lastDescriptorWasClosed = false; 495 uint16 highestMinimumUDFReadRevision = 0x0000; 496 status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND; 497 for (uint32 i = 0; error == B_OK && i < count; i++) { 498 off_t block = descriptorSequence.location()+i; 499 off_t address = block << blockShift; 500 MemoryChunk chunk(blockSize); 501 descriptor_tag *tag = NULL; 502 503 PRINT(("integrity descriptor #%" B_PRIu32 ":%" B_PRIu32 504 " (block %" B_PRIdOFF "):\n", 505 sequenceNumber, i, block)); 506 507 status_t loopError = chunk.InitCheck(); 508 if (!loopError) { 509 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 510 loopError = check_size_error(bytesRead, blockSize); 511 if (loopError) { 512 PRINT(("block %" B_PRIdOFF": read_pos(pos:%" B_PRIdOFF 513 ", len:%" B_PRIu32 ") failed with error 0x%lx\n", 514 block, address, blockSize, bytesRead)); 515 } 516 } 517 if (!loopError) { 518 tag = reinterpret_cast<descriptor_tag *>(chunk.Data()); 519 loopError = tag->init_check(block); 520 } 521 if (!loopError) { 522 // Check the descriptor type and see if it's closed. 523 loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR 524 ? B_OK : B_BAD_DATA; 525 if (!loopError) { 526 logical_volume_integrity_descriptor *descriptor = 527 reinterpret_cast<logical_volume_integrity_descriptor*>(chunk.Data()); 528 PDUMP(descriptor); 529 lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED; 530 if (lastDescriptorWasClosed) { 531 uint16 minimumRevision = descriptor->minimum_udf_read_revision(); 532 if (minimumRevision > highestMinimumUDFReadRevision) { 533 highestMinimumUDFReadRevision = minimumRevision; 534 } else if (minimumRevision < highestMinimumUDFReadRevision) { 535 INFORM(("WARNING: found decreasing minimum udf read revision in integrity " 536 "sequence (last highest: 0x%04x, current: 0x%04x); using higher " 537 "revision.\n", highestMinimumUDFReadRevision, minimumRevision)); 538 } 539 } 540 541 // Check a continuation extent if necessary. Note that this effectively 542 // ends our search through this extent 543 extent_address &next = descriptor->next_integrity_extent(); 544 if (next.length() > 0) { 545 status_t nextError = walk_integrity_sequence(device, blockSize, blockShift, 546 next, sequenceNumber+1); 547 if (nextError && nextError != B_ENTRY_NOT_FOUND) { 548 // Continuation proved invalid 549 error = nextError; 550 break; 551 } else { 552 // Either the continuation was valid or empty; either way, 553 // we're done searching. 554 break; 555 } 556 } 557 } else { 558 PDUMP(tag); 559 } 560 } 561 // If we hit an error on the first item, consider the extent empty, 562 // otherwise just break out of the loop and assume part of the 563 // extent is unrecorded 564 if (loopError) { 565 if (i == 0) 566 error = B_ENTRY_NOT_FOUND; 567 else 568 break; 569 } 570 } 571 if (!error) 572 error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA; 573 if (error == B_OK 574 && highestMinimumUDFReadRevision > UDF_MAX_READ_REVISION) { 575 error = B_ERROR; 576 FATAL(("found udf revision 0x%x more than max 0x%x\n", 577 highestMinimumUDFReadRevision, UDF_MAX_READ_REVISION)); 578 } 579 RETURN(error); 580 } 581