From c039b2c676e43100da1642d2cf5a83ebb5c8255b Mon Sep 17 00:00:00 2001 From: Tyler Dauwalder Date: Sun, 2 Nov 2003 06:50:40 +0000 Subject: [PATCH] Initial checkin. - Started migration of volume recognition code from Udf::Volume class to stateless functions in Recognition.{h,cpp} - Made partition recognition code more strictly adherant to UDF specs. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5236 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/udf/Recognition.cpp | 407 ++++++++++++++++++ .../kernel/file_systems/udf/Recognition.h | 28 ++ 2 files changed, 435 insertions(+) create mode 100644 src/add-ons/kernel/file_systems/udf/Recognition.cpp create mode 100644 src/add-ons/kernel/file_systems/udf/Recognition.h diff --git a/src/add-ons/kernel/file_systems/udf/Recognition.cpp b/src/add-ons/kernel/file_systems/udf/Recognition.cpp new file mode 100644 index 0000000000..1d95df6166 --- /dev/null +++ b/src/add-ons/kernel/file_systems/udf/Recognition.cpp @@ -0,0 +1,407 @@ +#include "Recognition.h" + +#include "MemoryChunk.h" + +using namespace Udf; + +//------------------------------------------------------------------------------ +// forward declarations +//------------------------------------------------------------------------------ + +static status_t +walk_volume_recognition_sequence(int device, off_t offset, + uint32 blockSize, + uint32 blockShift); +static status_t +walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, + uint32 blockSize, uint32 blockShift, + udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); +static status_t +walk_volume_descriptor_sequence(udf_extent_address descriptorSequence, + int device, uint32 blockSize, uint32 blockShift, + udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); + +//------------------------------------------------------------------------------ +// externally visible functions +//------------------------------------------------------------------------------ + +status_t +Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, + uint32 &blockShift, udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT(CF_PRIVATE, NULL); + + // Check the block size + uint32 bitCount = 0; + for (int i = 0; i < 32; i++) { + // Zero out all bits except bit i + uint32 block = blockSize & (uint32(1) << i); + if (block) { + if (++bitCount > 1) { + PRINT(("Block size must be a power of two! (blockSize = %ld)\n", blockSize)); + RETURN(B_BAD_VALUE); + } else { + blockShift = i; + PRINT(("blockShift: %ld\n", blockShift)); + } + } + } + + // Check for a valid volume recognition sequence + status_t err = walk_volume_recognition_sequence(device, offset, blockSize, blockShift); + + // Now hunt down a volume descriptor sequence from one of + // the anchor volume pointers (if there are any). + if (!err) + err = walk_anchor_volume_descriptor_sequences(device, offset, length, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + + RETURN(err); +} + +status_t +Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, + uint32 &blockShift) +{ + udf_logical_descriptor logicalVolumeDescriptor; + udf_partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors]; + uint8 partitionDescriptorCount; + return udf_recognize(device, offset, length, blockSize, blockShift, + logicalVolumeDescriptor, partitionDescriptors, + partitionDescriptorCount); +} + +//------------------------------------------------------------------------------ +// local functions +//------------------------------------------------------------------------------ + +static +status_t +walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, uint32 blockShift) +{ + DEBUG_INIT(CF_PRIVATE, NULL); + // vrs starts at block 16. Each volume structure descriptor (vsd) + // should be one block long. We're expecting to find 0 or more iso9660 + // vsd's followed by some ECMA-167 vsd's. + MemoryChunk chunk(blockSize); + status_t err = chunk.InitCheck(); + if (!err) { + bool foundISO = false; + bool foundExtended = false; + bool foundECMA167 = false; + bool foundECMA168 = false; + bool foundBoot = false; + for (uint32 block = 16; true; block++) { + PRINT(("block %ld: ", block)) + off_t address = (offset + block) << blockShift; + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + if (bytesRead == (ssize_t)blockSize) + { + udf_volume_structure_descriptor_header* descriptor = + reinterpret_cast(chunk.Data()); + if (descriptor->id_matches(kVSDID_ISO)) { + SIMPLE_PRINT(("found ISO9660 descriptor\n")); + foundISO = true; + } else if (descriptor->id_matches(kVSDID_BEA)) { + SIMPLE_PRINT(("found BEA descriptor\n")); + foundExtended = true; + } else if (descriptor->id_matches(kVSDID_TEA)) { + SIMPLE_PRINT(("found TEA descriptor\n")); + foundExtended = true; + } else if (descriptor->id_matches(kVSDID_ECMA167_2)) { + SIMPLE_PRINT(("found ECMA-167 rev 2 descriptor\n")); + foundECMA167 = true; + } else if (descriptor->id_matches(kVSDID_ECMA167_3)) { + SIMPLE_PRINT(("found ECMA-167 rev 3 descriptor\n")); + foundECMA167 = true; + } else if (descriptor->id_matches(kVSDID_BOOT)) { + SIMPLE_PRINT(("found boot descriptor\n")); + foundBoot = true; + } else if (descriptor->id_matches(kVSDID_ECMA168)) { + SIMPLE_PRINT(("found ECMA-168 descriptor\n")); + foundECMA168 = true; + } else { + SIMPLE_PRINT(("found invalid descriptor, id = `%.5s'\n", descriptor->id)); + break; + } + } else { + SIMPLE_PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", address, + blockSize, bytesRead)); + break; + } + } + + // If we find an ECMA-167 descriptor, OR if we find a beginning + // or terminating extended area descriptor with NO ECMA-168 + // descriptors, we return B_OK to signal that we should go + // looking for valid anchors. + err = foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR; + } + + RETURN(err); +} + +static +status_t +walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, + uint32 blockSize, uint32 blockShift, + udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT(CF_PRIVATE, NULL); + const uint8 avds_location_count = 4; + const off_t avds_locations[avds_location_count] = { + 256, + length-256, + length, + 512, + }; + bool found_vds = false; + for (int32 i = 0; i < avds_location_count; i++) { + off_t block = avds_locations[i]; + off_t address = (offset + block) << blockShift; + MemoryChunk chunk(blockSize); + udf_anchor_descriptor *anchor = NULL; + + status_t anchorErr = chunk.InitCheck(); + if (!anchorErr) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; + if (anchorErr) { + PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", + block, address, blockSize, bytesRead)); + } + } + if (!anchorErr) { + anchor = reinterpret_cast(chunk.Data()); + anchorErr = anchor->tag().init_check(block+offset); + if (anchorErr) { + PRINT(("block %Ld: invalid anchor\n", block)); + } else { + PRINT(("block %Ld: valid anchor\n", block)); + } + } + if (!anchorErr) { + PRINT(("block %Ld: anchor:\n", block)); + PDUMP(anchor); + // Found an avds, so try the main sequence first, then + // the reserve sequence if the main one fails. + anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), device, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + if (anchorErr) + anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), device, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + } + if (!anchorErr) { + PRINT(("block %Ld: found valid vds\n", avds_locations[i])); + found_vds = true; + break; + } //else { + // Both failed, so loop around and try another avds +// PRINT(("block %Ld: vds search failed\n", avds_locations[i])); +// } + } + status_t err = found_vds ? B_OK : B_ERROR; + RETURN(err); +} + +static +status_t +walk_volume_descriptor_sequence(udf_extent_address descriptorSequence, + int device, uint32 blockSize, uint32 blockShift, + udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT_ETC(CF_PRIVATE, NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", + descriptorSequence.location(), descriptorSequence.length())); + uint32 count = descriptorSequence.length() >> blockShift; + + bool foundLogicalVolumeDescriptor = false; + uint8 uniquePartitions = 0; + status_t err = B_OK; + + for (uint32 i = 0; i < count; i++) + { + off_t block = descriptorSequence.location()+i; + off_t address = block << blockShift; //AddressForRelativeBlock(block); + MemoryChunk chunk(blockSize); + udf_tag *tag = NULL; + + PRINT(("descriptor #%ld (block %Ld):\n", i, block)); + + status_t loopErr = chunk.InitCheck(); + if (!loopErr) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + loopErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; + if (loopErr) { + PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", + block, address, blockSize, bytesRead)); + } + } + if (!loopErr) { + tag = reinterpret_cast(chunk.Data()); + loopErr = tag->init_check(block); + } + if (!loopErr) { + // Now decide what type of descriptor we have + switch (tag->id()) { + case TAGID_UNDEFINED: + break; + + case TAGID_PRIMARY_VOLUME_DESCRIPTOR: + { + udf_primary_descriptor *primary = reinterpret_cast(tag); + PDUMP(primary); + break; + } + + case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: + break; + + case TAGID_VOLUME_DESCRIPTOR_POINTER: + break; + + case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: + { + udf_implementation_use_descriptor *imp_use = reinterpret_cast(tag); + PDUMP(imp_use); + break; + } + + case TAGID_PARTITION_DESCRIPTOR: + { + udf_partition_descriptor *partition = reinterpret_cast(tag); + PDUMP(partition); + if (partition->tag().init_check(block) == B_OK) { + // Check for a previously discovered partition descriptor with + // the same number as this partition. If found, keep the one with + // the higher vds number. + bool foundDuplicate = false; + int i; + for (i = 0; i < uniquePartitions; i++) { + if (partitionDescriptors[i].partition_number() + == partition->partition_number()) + { + foundDuplicate = true; + if (partitionDescriptors[i].vds_number() + < partition->vds_number()) + { + partitionDescriptors[i] = *partition; + PRINT(("Replacing previous partition #%d (vds_number: %ld) with " + "new partition #%d (vds_number: %ld)\n", + partitionDescriptors[i].partition_number(), + partitionDescriptors[i].vds_number(), + partition->partition_number(), + partition->vds_number())); + } + break; + } + } + // If we didn't find a duplicate, see if we have any open descriptor + // spaces left. + if (!foundDuplicate) { + if (i < Udf::kMaxPartitionDescriptors) { + // At least one more partition descriptor allowed + partitionDescriptors[i] = *partition; + uniquePartitions++; + PRINT(("Adding partition #%d (vds_number: %ld)\n", + partition->partition_number(), + partition->vds_number())); + } else { + // We've found more than kMaxPartitionDescriptor uniquely- + // numbered partitions. So, search through the partitions + // we already have again, this time just looking for a + // partition with a lower vds number. If we find one, + // replace it with this one. If we don't, scream bloody + // murder. + bool foundReplacement = false; + for (int j = 0; j < uniquePartitions; j++) { + if (partitionDescriptors[j].vds_number() + < partition->vds_number()) + { + foundReplacement = true; + partitionDescriptors[j] = *partition; + PRINT(("Replacing partition #%d (vds_number: %ld) " + "with partition #%d (vds_number: %ld)\n", + partitionDescriptors[j].partition_number(), + partitionDescriptors[j].vds_number(), + partition->partition_number(), + partition->vds_number())); + break; + } + } + if (!foundReplacement) { + PRINT(("Found more than kMaxPartitionDescriptors == %d " + "unique partition descriptors!\n", + kMaxPartitionDescriptors)); + err = B_BAD_VALUE; + break; + } + } + } + } + break; + } + + case TAGID_LOGICAL_VOLUME_DESCRIPTOR: + { + udf_logical_descriptor *logical = reinterpret_cast(tag); + PDUMP(logical); + if (foundLogicalVolumeDescriptor) { + // Keep the vd with the highest vds_number + if (logicalVolumeDescriptor.vds_number() < logical->vds_number()) + logicalVolumeDescriptor = *logical; + } else { + logicalVolumeDescriptor = *logical; + foundLogicalVolumeDescriptor = true; + } + break; + } + + case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: + { + udf_unallocated_space_descriptor *unallocated = reinterpret_cast(tag); + PDUMP(unallocated); + break; + } + + case TAGID_TERMINATING_DESCRIPTOR: + { + udf_terminating_descriptor *terminating = reinterpret_cast(tag); + PDUMP(terminating); + break; + } + + case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR: + // Not found in this descriptor sequence + break; + + default: + break; + + } + } + } + + if (!err) + err = foundLogicalVolumeDescriptor ? B_OK : B_ERROR; + RETURN(err); +} + diff --git a/src/add-ons/kernel/file_systems/udf/Recognition.h b/src/add-ons/kernel/file_systems/udf/Recognition.h new file mode 100644 index 0000000000..75ff04731e --- /dev/null +++ b/src/add-ons/kernel/file_systems/udf/Recognition.h @@ -0,0 +1,28 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_RECOGNITION_H +#define _UDF_RECOGNITION_H + +/*! \file Recognition.h +*/ + +#include "DiskStructures.h" +#include "UdfDebug.h" + +namespace Udf { + +status_t udf_recognize(int device, off_t offset, off_t length, + uint32 blockSize, uint32 &blockShift, + udf_logical_descriptor &logicalVolumeDescriptor, + udf_partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); +status_t udf_recognize(int device, off_t offset, off_t length, + uint32 blockSize, uint32 &blockShift); + +} // namespace Udf + +#endif // _UDF_RECOGNITION_H