#include "Volume.h"
#include "Icb.h"
#include "MemoryChunk.h"
#include "MetadataPartition.h"
#include "PhysicalPartition.h"
#include "Recognition.h"
extern fs_volume_ops gUDFVolumeOps;
extern fs_vnode_ops gUDFVnodeOps;
Volume::Volume(fs_volume *fsVolume)
:
fBlockCache(NULL),
fBlockShift(0),
fBlockSize(0),
fDevice(-1),
fFSVolume(fsVolume),
fLength(0),
fMounted(false),
fOffset(0),
fRootIcb(NULL)
{
for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
fPartitions[i] = NULL;
}
Volume::~Volume()
{
_Unset();
}
status_t
Volume::Mount(const char *deviceName, off_t offset, off_t length,
uint32 blockSize, uint32 flags)
{
TRACE(("Volume::Mount: deviceName = `%s', offset = %" B_PRIdOFF ", length "
"= %" B_PRIdOFF ", blockSize: %" B_PRIu32 ", flags: %" B_PRIu32 "\n",
deviceName, offset, length, blockSize, flags));
if (!deviceName)
return B_BAD_VALUE;
if (Mounted())
return B_BUSY;
int device = open(deviceName, O_RDONLY);
if (device < B_OK) {
TRACE_ERROR(("Volume::Mount: failed to open device = %s\n", deviceName));
return device;
}
DEBUG_INIT_ETC("Volume", ("deviceName: %s", deviceName));
status_t status = B_OK;
#if 0
struct stat stat;
error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
if (!error) {
if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
DIE(("Unable to disable cache of underlying file system.\n"));
}
}
#endif
logical_volume_descriptor logicalVolumeDescriptor;
partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
uint8 partitionDescriptorCount;
uint32 blockShift;
status = udf_recognize(device, offset, length, blockSize, blockShift,
fPrimaryVolumeDescriptor, logicalVolumeDescriptor,
partitionDescriptors, partitionDescriptorCount);
if (!status) {
TRACE(("Volume::Mount: partition recognized\n"));
fBlockCache = block_cache_create(device, length, blockSize, IsReadOnly());
} else {
TRACE_ERROR(("Volume::Mount: failed to recognize partition\n"));
return status;
}
int physicalCount = 0;
int virtualCount = 0;
int sparableCount = 0;
int metadataCount = 0;
offset = 0;
for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count()
&& !status; i++)
{
uint8 *maps = logicalVolumeDescriptor.partition_maps();
partition_map_header *header = (partition_map_header *)(maps + offset);
TRACE(("Volume::Mount: partition map %d (type %d):\n", i,
header->type()));
if (header->type() == 1) {
TRACE(("Volume::Mount: map type -> physical\n"));
physical_partition_map* map = (physical_partition_map *)header;
partition_descriptor *descriptor = NULL;
for (uint8 j = 0; j < partitionDescriptorCount; j++) {
if (map->partition_number() ==
partitionDescriptors[j].partition_number()) {
descriptor = &partitionDescriptors[j];
break;
}
}
if (descriptor) {
PhysicalPartition *partition
= new(nothrow) PhysicalPartition(map->partition_number(),
descriptor->start(), descriptor->length());
status = partition ? B_OK : B_NO_MEMORY;
if (!status) {
TRACE(("Volume::Mount: adding PhysicalPartition(number: %d, "
"start: %" B_PRIu32 ", length: %" B_PRIu32 ")\n",
map->partition_number(), descriptor->start(),
descriptor->length()));
status = _SetPartition(i, partition);
if (!status)
physicalCount++;
}
} else {
TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
status = B_ERROR;
}
} else if (header->type() == 2) {
const entity_id &typeId = header->partition_type_id();
DUMP(typeId);
DUMP(kSparablePartitionMapId);
if (typeId.matches(kVirtualPartitionMapId)) {
TRACE(("map type: virtual\n"));
virtual_partition_map* map =
reinterpret_cast<virtual_partition_map*>(header);
virtualCount++;
(void)map;
} else if (typeId.matches(kSparablePartitionMapId)) {
TRACE(("map type: sparable\n"));
sparable_partition_map* map =
reinterpret_cast<sparable_partition_map*>(header);
sparableCount++;
(void)map;
} else if (typeId.matches(kMetadataPartitionMapId)) {
TRACE(("map type: metadata\n"));
metadata_partition_map* map =
reinterpret_cast<metadata_partition_map*>(header);
partition_descriptor *descriptor = NULL;
for (uint8 j = 0; j < partitionDescriptorCount; j++) {
if (map->partition_number() ==
partitionDescriptors[j].partition_number()) {
descriptor = &partitionDescriptors[j];
break;
}
}
Partition *parent = _GetPartition(map->partition_number());
if (descriptor != NULL && parent != NULL) {
MetadataPartition *partition
= new(nothrow) MetadataPartition(this,
map->partition_number(), *parent,
map->metadata_file_location(),
map->metadata_mirror_file_location(),
map->metadata_bitmap_file_location(),
map->allocation_unit_size(),
map->alignment_unit_size(),
map->flags() & 1);
status = partition ? partition->InitCheck() : B_NO_MEMORY;
if (!status) {
TRACE(("Volume::Mount: adding MetadataPartition()"));
status = _SetPartition(i, partition);
if (status == B_OK)
metadataCount++;
} else {
TRACE_ERROR(("Volume::Mount: metadata partition "
"creation failed! 0x%" B_PRIx32 "\n", status));
}
} else {
TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
status = B_ERROR;
}
} else {
TRACE(("map type: unrecognized (`%.23s')\n",
typeId.identifier()));
status = B_ERROR;
}
} else {
TRACE_ERROR(("Invalid partition type %d found!\n", header->type()));
status = B_ERROR;
}
offset += header->length();
}
if (!status) {
status = (physicalCount == 1 && virtualCount == 0
&& sparableCount == 0)
|| (physicalCount == 2 && virtualCount == 0
&& sparableCount == 0)
? B_OK : B_ERROR;
if (status) {
TRACE_ERROR(("Invalid partition layout found:\n"));
TRACE_ERROR((" physical partitions: %d\n", physicalCount));
TRACE_ERROR((" virtual partitions: %d\n", virtualCount));
TRACE_ERROR((" sparable partitions: %d\n", sparableCount));
TRACE_ERROR((" metadata partitions: %d\n", metadataCount));
}
}
if (!status) {
fDevice = device;
fOffset = offset;
fLength = length;
fBlockSize = blockSize;
fBlockShift = blockShift;
}
TRACE(("Volume::Mount: device = %d, offset = %" B_PRIdOFF ", length = %"
B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n",
device, offset, length, blockSize, blockShift));
if (!status) {
TRACE(("Volume::Mount: Partition has been set up\n"));
MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
status = chunk.InitCheck();
if (!status) {
off_t address;
status = MapBlock(logicalVolumeDescriptor.file_set_address(),
&address);
if (!status)
address <<= blockShift;
if (!status) {
ssize_t bytesRead
= read_pos(device, address, chunk.Data(), blockSize);
if (bytesRead != ssize_t(blockSize)) {
status = B_IO_ERROR;
TRACE_ERROR(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32
") failed with: 0x%lx\n", address, blockSize,
bytesRead));
}
}
if (!status) {
file_set_descriptor *fileSet =
reinterpret_cast<file_set_descriptor*>(chunk.Data());
PDUMP(fileSet);
status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
? B_OK : B_ERROR;
if (!status)
status = fileSet->tag().init_check(
logicalVolumeDescriptor.file_set_address().block());
if (!status) {
PDUMP(fileSet);
fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb());
if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK)
return B_NO_MEMORY;
}
TRACE(("Volume::Mount: Root Node id = %" B_PRIdINO "\n",
fRootIcb->Id()));
if (!status) {
status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb,
&gUDFVnodeOps, fRootIcb->Mode(), 0);
if (status != B_OK) {
TRACE_ERROR(("Error creating vnode for root icb! "
"status = 0x%" B_PRIx32 ", `%s'\n", status,
strerror(status)));
delete fRootIcb;
fRootIcb = NULL;
}
TRACE(("Volume::Mount: Root vnode published. Id = %"
B_PRIdINO "\n", fRootIcb->Id()));
}
}
}
}
if (!status) {
fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
fMounted = true;
} else {
_Unset();
}
RETURN(status);
}
const char*
Volume::Name() const {
return fName.Utf8();
}
status_t
Volume::MapBlock(long_address address, off_t *mappedBlock)
{
TRACE(("Volume::MapBlock: partition = %d, block = %" B_PRIu32
", mappedBlock = %p\n", address.partition(), address.block(),
mappedBlock));
DEBUG_INIT_ETC("Volume", ("partition = %d, block = %" B_PRIu32
", mappedBlock = %p", address.partition(), address.block(),
mappedBlock));
status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
if (!error) {
Partition *partition = _GetPartition(address.partition());
error = partition ? B_OK : B_BAD_ADDRESS;
if (!error)
error = partition->MapBlock(address.block(), *mappedBlock);
}
RETURN(error);
}
void
Volume::_Unset()
{
DEBUG_INIT("Volume");
for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
_SetPartition(i, NULL);
fFSVolume->id = 0;
if (fDevice >= 0) {
block_cache_delete(fBlockCache, true);
close(fDevice);
}
fBlockCache = NULL;
fDevice = -1;
fMounted = false;
fOffset = 0;
fLength = 0;
fBlockSize = 0;
fBlockShift = 0;
fName.SetTo("", 0);
}
status_t
Volume::_SetPartition(uint number, Partition *partition)
{
status_t error = number < UDF_MAX_PARTITION_MAPS
? B_OK : B_BAD_VALUE;
if (!error) {
delete fPartitions[number];
fPartitions[number] = partition;
}
return error;
}
Partition*
Volume::_GetPartition(uint number)
{
return (number < UDF_MAX_PARTITION_MAPS)
? fPartitions[number] : NULL;
}