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