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