* Copyright 2012, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com
* Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
* Distributed under the terms of the MIT License.
*/
#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();
}
\param lenght The length of the device in number of blocks
*/
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(("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(("Invalid partition layout found:\n"));
TRACE((" physical partitions: %d\n", physicalCount));
TRACE((" virtual partitions: %d\n", virtualCount));
TRACE((" sparable partitions: %d\n", sparableCount));
TRACE((" 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);
}
Does *not* delete the root icb object.
*/
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);
}
deleting any previously associated partition.
\param number The partition number (should be the same as the index
into the lvd's partition map array).
\param partition The new partition (may be NULL).
*/
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;
}
NULL if no such partition exists or the number is invalid.
*/
Partition*
Volume::_GetPartition(uint number)
{
return (number < UDF_MAX_PARTITION_MAPS)
? fPartitions[number] : NULL;
}