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_ERROR(("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 B_ERROR; 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_ERROR(("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 %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", 198 block, address, blockSize, bytesRead)); 199 } 200 } 201 if (!anchorErr) { 202 anchor = reinterpret_cast<anchor_volume_descriptor*>(chunk.Data()); 203 anchorErr = anchor->tag().init_check(block + offset); 204 if (anchorErr) { 205 PRINT(("block %Ld: invalid anchor\n", block)); 206 } else { 207 PRINT(("block %Ld: valid anchor\n", block)); 208 } 209 } 210 if (!anchorErr) { 211 PRINT(("block %Ld: anchor:\n", block)); 212 PDUMP(anchor); 213 // Found an avds, so try the main sequence first, then 214 // the reserve sequence if the main one fails. 215 anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), 216 device, blockSize, blockShift, primaryVolumeDescriptor, 217 logicalVolumeDescriptor, partitionDescriptors, 218 partitionDescriptorCount); 219 220 if (anchorErr) 221 anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), 222 device, blockSize, blockShift, primaryVolumeDescriptor, 223 logicalVolumeDescriptor, partitionDescriptors, 224 partitionDescriptorCount); 225 } 226 if (!anchorErr) { 227 PRINT(("block %Ld: found valid vds\n", avds_locations[i])); 228 found_vds = true; 229 break; 230 } else { 231 // Both failed, so loop around and try another avds 232 PRINT(("block %Ld: vds search failed\n", avds_locations[i])); 233 } 234 } 235 status_t error = found_vds ? B_OK : B_ERROR; 236 RETURN(error); 237 } 238 239 static 240 status_t 241 walk_volume_descriptor_sequence(extent_address descriptorSequence, 242 int device, uint32 blockSize, uint32 blockShift, 243 primary_volume_descriptor &primaryVolumeDescriptor, 244 logical_volume_descriptor &logicalVolumeDescriptor, 245 partition_descriptor partitionDescriptors[], 246 uint8 &partitionDescriptorCount) 247 { 248 DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", 249 descriptorSequence.location(), descriptorSequence.length())); 250 uint32 count = descriptorSequence.length() >> blockShift; 251 252 bool foundLogicalVolumeDescriptor = false; 253 bool foundUnallocatedSpaceDescriptor = false; 254 bool foundUdfImplementationUseDescriptor = false; 255 uint8 uniquePartitions = 0; 256 status_t error = B_OK; 257 258 for (uint32 i = 0; i < count; i++) 259 { 260 off_t block = descriptorSequence.location()+i; 261 off_t address = block << blockShift; 262 MemoryChunk chunk(blockSize); 263 descriptor_tag *tag = NULL; 264 265 PRINT(("descriptor #%ld (block %Ld):\n", i, block)); 266 267 status_t loopError = chunk.InitCheck(); 268 if (!loopError) { 269 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 270 loopError = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; 271 if (loopError) { 272 PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", 273 block, address, blockSize, bytesRead)); 274 } 275 } 276 if (!loopError) { 277 tag = reinterpret_cast<descriptor_tag *>(chunk.Data()); 278 loopError = tag->init_check(block); 279 } 280 if (!loopError) { 281 // Now decide what type of descriptor we have 282 switch (tag->id()) { 283 case TAGID_UNDEFINED: 284 break; 285 286 case TAGID_PRIMARY_VOLUME_DESCRIPTOR: 287 { 288 primary_volume_descriptor *primary = reinterpret_cast<primary_volume_descriptor*>(tag); 289 PDUMP(primary); 290 primaryVolumeDescriptor = *primary; 291 break; 292 } 293 294 case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: 295 break; 296 297 case TAGID_VOLUME_DESCRIPTOR_POINTER: 298 break; 299 300 case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: 301 { 302 implementation_use_descriptor *impUse = reinterpret_cast<implementation_use_descriptor*>(tag); 303 PDUMP(impUse); 304 // Check for a matching implementation id string (note that the 305 // revision version is not checked) 306 if (impUse->tag().init_check(block) == B_OK 307 && impUse->implementation_id().matches(kLogicalVolumeInfoId201)) 308 { 309 foundUdfImplementationUseDescriptor = true; 310 } 311 break; 312 } 313 314 case TAGID_PARTITION_DESCRIPTOR: 315 { 316 partition_descriptor *partition = reinterpret_cast<partition_descriptor*>(tag); 317 PDUMP(partition); 318 if (partition->tag().init_check(block) == B_OK) { 319 // Check for a previously discovered partition descriptor with 320 // the same number as this partition. If found, keep the one with 321 // the higher vds number. 322 bool foundDuplicate = false; 323 int num; 324 for (num = 0; num < uniquePartitions; num++) { 325 if (partitionDescriptors[num].partition_number() 326 == partition->partition_number()) 327 { 328 foundDuplicate = true; 329 if (partitionDescriptors[num].vds_number() 330 < partition->vds_number()) 331 { 332 partitionDescriptors[num] = *partition; 333 PRINT(("Replacing previous partition #%d (vds_number: %ld) with " 334 "new partition #%d (vds_number: %ld)\n", 335 partitionDescriptors[num].partition_number(), 336 partitionDescriptors[num].vds_number(), 337 partition->partition_number(), 338 partition->vds_number())); 339 } 340 break; 341 } 342 } 343 // If we didn't find a duplicate, see if we have any open descriptor 344 // spaces left. 345 if (!foundDuplicate) { 346 if (num < kMaxPartitionDescriptors) { 347 // At least one more partition descriptor allowed 348 partitionDescriptors[num] = *partition; 349 uniquePartitions++; 350 PRINT(("Adding partition #%d (vds_number: %ld)\n", 351 partition->partition_number(), 352 partition->vds_number())); 353 } else { 354 // We've found more than kMaxPartitionDescriptor uniquely- 355 // numbered partitions. So, search through the partitions 356 // we already have again, this time just looking for a 357 // partition with a lower vds number. If we find one, 358 // replace it with this one. If we don't, scream bloody 359 // murder. 360 bool foundReplacement = false; 361 for (int j = 0; j < uniquePartitions; j++) { 362 if (partitionDescriptors[j].vds_number() 363 < partition->vds_number()) 364 { 365 foundReplacement = true; 366 partitionDescriptors[j] = *partition; 367 PRINT(("Replacing partition #%d (vds_number: %ld) " 368 "with partition #%d (vds_number: %ld)\n", 369 partitionDescriptors[j].partition_number(), 370 partitionDescriptors[j].vds_number(), 371 partition->partition_number(), 372 partition->vds_number())); 373 break; 374 } 375 } 376 if (!foundReplacement) { 377 PRINT(("Found more than kMaxPartitionDescriptors == %d " 378 "unique partition descriptors!\n", 379 kMaxPartitionDescriptors)); 380 error = B_BAD_VALUE; 381 break; 382 } 383 } 384 } 385 } 386 break; 387 } 388 389 case TAGID_LOGICAL_VOLUME_DESCRIPTOR: 390 { 391 logical_volume_descriptor *logical = reinterpret_cast<logical_volume_descriptor*>(tag); 392 PDUMP(logical); 393 if (foundLogicalVolumeDescriptor) { 394 // Keep the vd with the highest vds_number 395 if (logicalVolumeDescriptor.vds_number() < logical->vds_number()) 396 logicalVolumeDescriptor = *logical; 397 } else { 398 logicalVolumeDescriptor = *logical; 399 foundLogicalVolumeDescriptor = true; 400 } 401 break; 402 } 403 404 case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: 405 { 406 unallocated_space_descriptor *unallocated = reinterpret_cast<unallocated_space_descriptor*>(tag); 407 PDUMP(unallocated); 408 foundUnallocatedSpaceDescriptor = true; 409 (void)unallocated; // kill the warning 410 break; 411 } 412 413 case TAGID_TERMINATING_DESCRIPTOR: 414 { 415 terminating_descriptor *terminating = reinterpret_cast<terminating_descriptor*>(tag); 416 PDUMP(terminating); 417 (void)terminating; // kill the warning 418 break; 419 } 420 421 case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR: 422 // Not found in this descriptor sequence 423 break; 424 425 default: 426 break; 427 428 } 429 } 430 } 431 432 PRINT(("found %d unique partition%s\n", uniquePartitions, 433 (uniquePartitions == 1 ? "" : "s"))); 434 435 if (!error && !foundUdfImplementationUseDescriptor) { 436 INFORM(("WARNING: no valid udf implementation use descriptor found\n")); 437 } 438 if (!error) 439 error = foundLogicalVolumeDescriptor 440 && foundUnallocatedSpaceDescriptor 441 ? B_OK : B_ERROR; 442 if (!error) 443 error = uniquePartitions >= 1 ? B_OK : B_ERROR; 444 if (!error) 445 partitionDescriptorCount = uniquePartitions; 446 447 RETURN(error); 448 } 449 450 /*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence. 451 452 \return 453 - \c B_OK: Success. the sequence was terminated by a valid, closed 454 integrity descriptor. 455 - \c B_ENTRY_NOT_FOUND: The sequence was empty. 456 - (other error code): The sequence was non-empty and did not end in a valid, 457 closed integrity descriptor. 458 */ 459 static status_t 460 walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift, 461 extent_address descriptorSequence, uint32 sequenceNumber) 462 { 463 DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", 464 descriptorSequence.location(), descriptorSequence.length())); 465 uint32 count = descriptorSequence.length() >> blockShift; 466 467 bool lastDescriptorWasClosed = false; 468 uint16 highestMinimumUDFReadRevision = 0x0000; 469 status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND; 470 for (uint32 i = 0; error == B_OK && i < count; i++) 471 { 472 off_t block = descriptorSequence.location()+i; 473 off_t address = block << blockShift; 474 MemoryChunk chunk(blockSize); 475 descriptor_tag *tag = NULL; 476 477 PRINT(("integrity descriptor #%ld:%ld (block %Ld):\n", sequenceNumber, i, block)); 478 479 status_t loopError = chunk.InitCheck(); 480 if (!loopError) { 481 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 482 loopError = check_size_error(bytesRead, blockSize); 483 if (loopError) { 484 PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", 485 block, address, blockSize, bytesRead)); 486 } 487 } 488 if (!loopError) { 489 tag = reinterpret_cast<descriptor_tag *>(chunk.Data()); 490 loopError = tag->init_check(block); 491 } 492 if (!loopError) { 493 // Check the descriptor type and see if it's closed. 494 loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR 495 ? B_OK : B_BAD_DATA; 496 if (!loopError) { 497 logical_volume_integrity_descriptor *descriptor = 498 reinterpret_cast<logical_volume_integrity_descriptor*>(chunk.Data()); 499 PDUMP(descriptor); 500 lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED; 501 if (lastDescriptorWasClosed) { 502 uint16 minimumRevision = descriptor->minimum_udf_read_revision(); 503 if (minimumRevision > highestMinimumUDFReadRevision) { 504 highestMinimumUDFReadRevision = minimumRevision; 505 } else if (minimumRevision < highestMinimumUDFReadRevision) { 506 INFORM(("WARNING: found decreasing minimum udf read revision in integrity " 507 "sequence (last highest: 0x%04x, current: 0x%04x); using higher " 508 "revision.\n", highestMinimumUDFReadRevision, minimumRevision)); 509 } 510 } 511 512 // Check a continuation extent if necessary. Note that this effectively 513 // ends our search through this extent 514 extent_address &next = descriptor->next_integrity_extent(); 515 if (next.length() > 0) { 516 status_t nextError = walk_integrity_sequence(device, blockSize, blockShift, 517 next, sequenceNumber+1); 518 if (nextError && nextError != B_ENTRY_NOT_FOUND) { 519 // Continuation proved invalid 520 error = nextError; 521 break; 522 } else { 523 // Either the continuation was valid or empty; either way, 524 // we're done searching. 525 break; 526 } 527 } 528 } else { 529 PDUMP(tag); 530 } 531 } 532 // If we hit an error on the first item, consider the extent empty, 533 // otherwise just break out of the loop and assume part of the 534 // extent is unrecorded 535 if (loopError) { 536 if (i == 0) 537 error = B_ENTRY_NOT_FOUND; 538 else 539 break; 540 } 541 } 542 if (!error) 543 error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA; 544 if (error == B_OK 545 && highestMinimumUDFReadRevision > UDF_MAX_READ_REVISION) { 546 error = B_ERROR; 547 FATAL(("found udf revision 0x%x more than max 0x%x\n", 548 highestMinimumUDFReadRevision, UDF_MAX_READ_REVISION)); 549 } 550 RETURN(error); 551 } 552