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