1 #include "Recognition.h" 2 3 #include "CS0String.h" 4 #include "MemoryChunk.h" 5 6 using namespace Udf; 7 8 //------------------------------------------------------------------------------ 9 // forward declarations 10 //------------------------------------------------------------------------------ 11 12 static status_t 13 walk_volume_recognition_sequence(int device, off_t offset, 14 uint32 blockSize, 15 uint32 blockShift); 16 static status_t 17 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, 18 uint32 blockSize, uint32 blockShift, 19 udf_logical_descriptor &logicalVolumeDescriptor, 20 udf_partition_descriptor partitionDescriptors[], 21 uint8 &partitionDescriptorCount); 22 static status_t 23 walk_volume_descriptor_sequence(udf_extent_address descriptorSequence, 24 int device, uint32 blockSize, uint32 blockShift, 25 udf_logical_descriptor &logicalVolumeDescriptor, 26 udf_partition_descriptor partitionDescriptors[], 27 uint8 &partitionDescriptorCount); 28 29 //------------------------------------------------------------------------------ 30 // externally visible functions 31 //------------------------------------------------------------------------------ 32 33 status_t 34 Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, 35 uint32 &blockShift, udf_logical_descriptor &logicalVolumeDescriptor, 36 udf_partition_descriptor partitionDescriptors[], 37 uint8 &partitionDescriptorCount) 38 { 39 DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("device: %d, offset: %Ld, length: %Ld, " 40 "blockSize: %ld, [...descriptors, etc...]", device, offset, 41 length, blockSize)); 42 43 // Check the block size 44 uint32 bitCount = 0; 45 for (int i = 0; i < 32; i++) { 46 // Zero out all bits except bit i 47 uint32 block = blockSize & (uint32(1) << i); 48 if (block) { 49 if (++bitCount > 1) { 50 PRINT(("Block size must be a power of two! (blockSize = %ld)\n", blockSize)); 51 RETURN(B_BAD_VALUE); 52 } else { 53 blockShift = i; 54 PRINT(("blockShift: %ld\n", blockShift)); 55 } 56 } 57 } 58 59 // Check for a valid volume recognition sequence 60 status_t error = walk_volume_recognition_sequence(device, offset, blockSize, blockShift); 61 62 // Now hunt down a volume descriptor sequence from one of 63 // the anchor volume pointers (if there are any). 64 if (!error) { 65 error = walk_anchor_volume_descriptor_sequences(device, offset, length, 66 blockSize, blockShift, 67 logicalVolumeDescriptor, 68 partitionDescriptors, 69 partitionDescriptorCount); 70 } 71 RETURN(error); 72 } 73 74 status_t 75 Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, 76 char *volumeName) 77 { 78 DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("device: %d, offset: %Ld, length: %Ld, " 79 "blockSize: %ld, volumeName: %p", device, offset, length, 80 blockSize, volumeName)); 81 udf_logical_descriptor logicalVolumeDescriptor; 82 udf_partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors]; 83 uint8 partitionDescriptorCount; 84 uint32 blockShift; 85 status_t error = udf_recognize(device, offset, length, blockSize, blockShift, 86 logicalVolumeDescriptor, partitionDescriptors, 87 partitionDescriptorCount); 88 if (!error && volumeName) { 89 CS0String name(logicalVolumeDescriptor.logical_volume_identifier()); 90 strcpy(volumeName, name.String()); 91 } 92 RETURN(error); 93 } 94 95 //------------------------------------------------------------------------------ 96 // local functions 97 //------------------------------------------------------------------------------ 98 99 static 100 status_t 101 walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, uint32 blockShift) 102 { 103 DEBUG_INIT(CF_PRIVATE, NULL); 104 // vrs starts at block 16. Each volume structure descriptor (vsd) 105 // should be one block long. We're expecting to find 0 or more iso9660 106 // vsd's followed by some ECMA-167 vsd's. 107 MemoryChunk chunk(blockSize); 108 status_t error = chunk.InitCheck(); 109 if (!error) { 110 bool foundISO = false; 111 bool foundExtended = false; 112 bool foundECMA167 = false; 113 bool foundECMA168 = false; 114 bool foundBoot = false; 115 for (uint32 block = 16; true; block++) { 116 PRINT(("block %ld: ", block)) 117 off_t address = (offset + block) << blockShift; 118 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 119 if (bytesRead == (ssize_t)blockSize) 120 { 121 udf_volume_structure_descriptor_header* descriptor = 122 reinterpret_cast<udf_volume_structure_descriptor_header*>(chunk.Data()); 123 if (descriptor->id_matches(kVSDID_ISO)) { 124 SIMPLE_PRINT(("found ISO9660 descriptor\n")); 125 foundISO = true; 126 } else if (descriptor->id_matches(kVSDID_BEA)) { 127 SIMPLE_PRINT(("found BEA descriptor\n")); 128 foundExtended = true; 129 } else if (descriptor->id_matches(kVSDID_TEA)) { 130 SIMPLE_PRINT(("found TEA descriptor\n")); 131 foundExtended = true; 132 } else if (descriptor->id_matches(kVSDID_ECMA167_2)) { 133 SIMPLE_PRINT(("found ECMA-167 rev 2 descriptor\n")); 134 foundECMA167 = true; 135 } else if (descriptor->id_matches(kVSDID_ECMA167_3)) { 136 SIMPLE_PRINT(("found ECMA-167 rev 3 descriptor\n")); 137 foundECMA167 = true; 138 } else if (descriptor->id_matches(kVSDID_BOOT)) { 139 SIMPLE_PRINT(("found boot descriptor\n")); 140 foundBoot = true; 141 } else if (descriptor->id_matches(kVSDID_ECMA168)) { 142 SIMPLE_PRINT(("found ECMA-168 descriptor\n")); 143 foundECMA168 = true; 144 } else { 145 SIMPLE_PRINT(("found invalid descriptor, id = `%.5s'\n", descriptor->id)); 146 break; 147 } 148 } else { 149 SIMPLE_PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", address, 150 blockSize, bytesRead)); 151 break; 152 } 153 } 154 155 // If we find an ECMA-167 descriptor, OR if we find a beginning 156 // or terminating extended area descriptor with NO ECMA-168 157 // descriptors, we return B_OK to signal that we should go 158 // looking for valid anchors. 159 error = foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR; 160 } 161 162 RETURN(error); 163 } 164 165 static 166 status_t 167 walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, 168 uint32 blockSize, uint32 blockShift, 169 udf_logical_descriptor &logicalVolumeDescriptor, 170 udf_partition_descriptor partitionDescriptors[], 171 uint8 &partitionDescriptorCount) 172 { 173 DEBUG_INIT(CF_PRIVATE, NULL); 174 const uint8 avds_location_count = 4; 175 const off_t avds_locations[avds_location_count] = { 176 256, 177 length-1-256, 178 length-1, 179 512, 180 }; 181 bool found_vds = false; 182 for (int32 i = 0; i < avds_location_count; i++) { 183 off_t block = avds_locations[i]; 184 off_t address = (offset + block) << blockShift; 185 MemoryChunk chunk(blockSize); 186 udf_anchor_descriptor *anchor = NULL; 187 188 status_t anchorErr = chunk.InitCheck(); 189 if (!anchorErr) { 190 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 191 anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; 192 if (anchorErr) { 193 PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", 194 block, address, blockSize, bytesRead)); 195 } 196 } 197 if (!anchorErr) { 198 anchor = reinterpret_cast<udf_anchor_descriptor*>(chunk.Data()); 199 anchorErr = anchor->tag().init_check(block+offset); 200 if (anchorErr) { 201 PRINT(("block %Ld: invalid anchor\n", block)); 202 } else { 203 PRINT(("block %Ld: valid anchor\n", block)); 204 } 205 } 206 if (!anchorErr) { 207 PRINT(("block %Ld: anchor:\n", block)); 208 PDUMP(anchor); 209 // Found an avds, so try the main sequence first, then 210 // the reserve sequence if the main one fails. 211 anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), device, 212 blockSize, blockShift, 213 logicalVolumeDescriptor, 214 partitionDescriptors, 215 partitionDescriptorCount); 216 if (anchorErr) 217 anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), device, 218 blockSize, blockShift, 219 logicalVolumeDescriptor, 220 partitionDescriptors, 221 partitionDescriptorCount); 222 } 223 if (!anchorErr) { 224 PRINT(("block %Ld: found valid vds\n", avds_locations[i])); 225 found_vds = true; 226 break; 227 } else { 228 // Both failed, so loop around and try another avds 229 PRINT(("block %Ld: vds search failed\n", avds_locations[i])); 230 } 231 } 232 status_t error = found_vds ? B_OK : B_ERROR; 233 RETURN(error); 234 } 235 236 static 237 status_t 238 walk_volume_descriptor_sequence(udf_extent_address descriptorSequence, 239 int device, uint32 blockSize, uint32 blockShift, 240 udf_logical_descriptor &logicalVolumeDescriptor, 241 udf_partition_descriptor partitionDescriptors[], 242 uint8 &partitionDescriptorCount) 243 { 244 DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", 245 descriptorSequence.location(), descriptorSequence.length())); 246 uint32 count = descriptorSequence.length() >> blockShift; 247 248 bool foundLogicalVolumeDescriptor = false; 249 uint8 uniquePartitions = 0; 250 status_t error = B_OK; 251 252 for (uint32 i = 0; i < count; i++) 253 { 254 off_t block = descriptorSequence.location()+i; 255 off_t address = block << blockShift; 256 MemoryChunk chunk(blockSize); 257 udf_tag *tag = NULL; 258 259 PRINT(("descriptor #%ld (block %Ld):\n", i, block)); 260 261 status_t loopErr = chunk.InitCheck(); 262 if (!loopErr) { 263 ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); 264 loopErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; 265 if (loopErr) { 266 PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", 267 block, address, blockSize, bytesRead)); 268 } 269 } 270 if (!loopErr) { 271 tag = reinterpret_cast<udf_tag*>(chunk.Data()); 272 loopErr = tag->init_check(block); 273 } 274 if (!loopErr) { 275 // Now decide what type of descriptor we have 276 switch (tag->id()) { 277 case TAGID_UNDEFINED: 278 break; 279 280 case TAGID_PRIMARY_VOLUME_DESCRIPTOR: 281 { 282 udf_primary_descriptor *primary = reinterpret_cast<udf_primary_descriptor*>(tag); 283 PDUMP(primary); 284 (void)primary; // kill the warning 285 break; 286 } 287 288 case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: 289 break; 290 291 case TAGID_VOLUME_DESCRIPTOR_POINTER: 292 break; 293 294 case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: 295 { 296 udf_implementation_use_descriptor *imp_use = reinterpret_cast<udf_implementation_use_descriptor*>(tag); 297 PDUMP(imp_use); 298 (void)imp_use; // kill the warning 299 break; 300 } 301 302 case TAGID_PARTITION_DESCRIPTOR: 303 { 304 udf_partition_descriptor *partition = reinterpret_cast<udf_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 < Udf::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 udf_logical_descriptor *logical = reinterpret_cast<udf_logical_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 DUMP(logicalVolumeDescriptor); 389 } 390 break; 391 } 392 393 case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: 394 { 395 udf_unallocated_space_descriptor *unallocated = reinterpret_cast<udf_unallocated_space_descriptor*>(tag); 396 PDUMP(unallocated); 397 (void)unallocated; // kill the warning 398 break; 399 } 400 401 case TAGID_TERMINATING_DESCRIPTOR: 402 { 403 udf_terminating_descriptor *terminating = reinterpret_cast<udf_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) 424 error = foundLogicalVolumeDescriptor ? B_OK : B_ERROR; 425 if (!error) 426 error = uniquePartitions >= 1 ? B_OK : B_ERROR; 427 if (!error) 428 partitionDescriptorCount = uniquePartitions; 429 430 RETURN(error); 431 } 432 433