1 /* 2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com 4 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 #include "Volume.h" 9 10 #include "Icb.h" 11 #include "MemoryChunk.h" 12 #include "MetadataPartition.h" 13 #include "PhysicalPartition.h" 14 #include "Recognition.h" 15 16 extern fs_volume_ops gUDFVolumeOps; 17 extern fs_vnode_ops gUDFVnodeOps; 18 19 /*! \brief Creates an unmounted volume with the given id. */ 20 Volume::Volume(fs_volume *fsVolume) 21 : 22 fBlockCache(NULL), 23 fBlockShift(0), 24 fBlockSize(0), 25 fDevice(-1), 26 fFSVolume(fsVolume), 27 fLength(0), 28 fMounted(false), 29 fOffset(0), 30 fRootIcb(NULL) 31 { 32 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) 33 fPartitions[i] = NULL; 34 } 35 36 37 Volume::~Volume() 38 { 39 _Unset(); 40 } 41 42 43 /*! \brief Attempts to mount the given device. 44 45 \param lenght The length of the device in number of blocks 46 */ 47 status_t 48 Volume::Mount(const char *deviceName, off_t offset, off_t length, 49 uint32 blockSize, uint32 flags) 50 { 51 TRACE(("Volume::Mount: deviceName = `%s', offset = %" B_PRIdOFF ", length " 52 "= %" B_PRIdOFF ", blockSize: %" B_PRIu32 ", flags: %" B_PRIu32 "\n", 53 deviceName, offset, length, blockSize, flags)); 54 if (!deviceName) 55 return B_BAD_VALUE; 56 if (Mounted()) 57 // Already mounted, thank you for asking 58 return B_BUSY; 59 60 // Open the device read only 61 int device = open(deviceName, O_RDONLY); 62 if (device < B_OK) { 63 TRACE_ERROR(("Volume::Mount: failed to open device = %s\n", deviceName)); 64 return device; 65 } 66 67 DEBUG_INIT_ETC("Volume", ("deviceName: %s", deviceName)); 68 status_t status = B_OK; 69 70 // If the device is actually a normal file, try to disable the cache 71 // for the file in the parent filesystem 72 #if 0 // _KERNEL_MODE 73 struct stat stat; 74 error = fstat(device, &stat) < 0 ? B_ERROR : B_OK; 75 if (!error) { 76 if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) { 77 DIE(("Unable to disable cache of underlying file system.\n")); 78 } 79 } 80 #endif 81 82 logical_volume_descriptor logicalVolumeDescriptor; 83 partition_descriptor partitionDescriptors[kMaxPartitionDescriptors]; 84 uint8 partitionDescriptorCount; 85 uint32 blockShift; 86 87 // Run through the volume recognition and descriptor sequences to 88 // see if we have a potentially valid UDF volume on our hands 89 status = udf_recognize(device, offset, length, blockSize, blockShift, 90 fPrimaryVolumeDescriptor, logicalVolumeDescriptor, 91 partitionDescriptors, partitionDescriptorCount); 92 93 // Set up the block cache 94 if (!status) { 95 TRACE(("Volume::Mount: partition recognized\n")); 96 fBlockCache = block_cache_create(device, length, blockSize, IsReadOnly()); 97 } else { 98 TRACE_ERROR(("Volume::Mount: failed to recognize partition\n")); 99 return status; 100 } 101 102 int physicalCount = 0; 103 int virtualCount = 0; 104 int sparableCount = 0; 105 int metadataCount = 0; 106 107 // Set up the partitions 108 // Set up physical and sparable partitions first 109 offset = 0; 110 for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count() 111 && !status; i++) 112 { 113 uint8 *maps = logicalVolumeDescriptor.partition_maps(); 114 partition_map_header *header = (partition_map_header *)(maps + offset); 115 TRACE(("Volume::Mount: partition map %d (type %d):\n", i, 116 header->type())); 117 if (header->type() == 1) { 118 TRACE(("Volume::Mount: map type -> physical\n")); 119 physical_partition_map* map = (physical_partition_map *)header; 120 // Find the corresponding partition descriptor 121 partition_descriptor *descriptor = NULL; 122 for (uint8 j = 0; j < partitionDescriptorCount; j++) { 123 if (map->partition_number() == 124 partitionDescriptors[j].partition_number()) { 125 descriptor = &partitionDescriptors[j]; 126 break; 127 } 128 } 129 // Create and add the partition 130 if (descriptor) { 131 PhysicalPartition *partition 132 = new(nothrow) PhysicalPartition(map->partition_number(), 133 descriptor->start(), descriptor->length()); 134 status = partition ? B_OK : B_NO_MEMORY; 135 if (!status) { 136 TRACE(("Volume::Mount: adding PhysicalPartition(number: %d, " 137 "start: %" B_PRIu32 ", length: %" B_PRIu32 ")\n", 138 map->partition_number(), descriptor->start(), 139 descriptor->length())); 140 status = _SetPartition(i, partition); 141 if (!status) 142 physicalCount++; 143 } 144 } else { 145 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n")); 146 status = B_ERROR; 147 } 148 } else if (header->type() == 2) { 149 // Figure out what kind of type 2 partition map we have based 150 // on the type identifier 151 const entity_id &typeId = header->partition_type_id(); 152 DUMP(typeId); 153 DUMP(kSparablePartitionMapId); 154 if (typeId.matches(kVirtualPartitionMapId)) { 155 TRACE(("map type: virtual\n")); 156 virtual_partition_map* map = 157 reinterpret_cast<virtual_partition_map*>(header); 158 virtualCount++; 159 (void)map; // kill the warning for now 160 } else if (typeId.matches(kSparablePartitionMapId)) { 161 TRACE(("map type: sparable\n")); 162 sparable_partition_map* map = 163 reinterpret_cast<sparable_partition_map*>(header); 164 sparableCount++; 165 (void)map; // kill the warning for now 166 } else if (typeId.matches(kMetadataPartitionMapId)) { 167 TRACE(("map type: metadata\n")); 168 metadata_partition_map* map = 169 reinterpret_cast<metadata_partition_map*>(header); 170 171 172 // Find the corresponding partition descriptor 173 partition_descriptor *descriptor = NULL; 174 for (uint8 j = 0; j < partitionDescriptorCount; j++) { 175 if (map->partition_number() == 176 partitionDescriptors[j].partition_number()) { 177 descriptor = &partitionDescriptors[j]; 178 break; 179 } 180 } 181 Partition *parent = _GetPartition(map->partition_number()); 182 // Create and add the partition 183 if (descriptor != NULL && parent != NULL) { 184 MetadataPartition *partition 185 = new(nothrow) MetadataPartition(this, 186 map->partition_number(), *parent, 187 map->metadata_file_location(), 188 map->metadata_mirror_file_location(), 189 map->metadata_bitmap_file_location(), 190 map->allocation_unit_size(), 191 map->alignment_unit_size(), 192 map->flags() & 1); 193 status = partition ? partition->InitCheck() : B_NO_MEMORY; 194 if (!status) { 195 TRACE(("Volume::Mount: adding MetadataPartition()")); 196 status = _SetPartition(i, partition); 197 if (status == B_OK) 198 metadataCount++; 199 } else { 200 TRACE_ERROR(("Volume::Mount: metadata partition " 201 "creation failed! 0x%" B_PRIx32 "\n", status)); 202 } 203 } else { 204 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n")); 205 status = B_ERROR; 206 } 207 } else { 208 TRACE(("map type: unrecognized (`%.23s')\n", 209 typeId.identifier())); 210 status = B_ERROR; 211 } 212 } else { 213 TRACE(("Invalid partition type %d found!\n", header->type())); 214 status = B_ERROR; 215 } 216 offset += header->length(); 217 } 218 219 // Do some checking as to what sorts of partitions we've actually found. 220 if (!status) { 221 status = (physicalCount == 1 && virtualCount == 0 222 && sparableCount == 0) 223 || (physicalCount == 2 && virtualCount == 0 224 && sparableCount == 0) 225 ? B_OK : B_ERROR; 226 if (status) { 227 TRACE(("Invalid partition layout found:\n")); 228 TRACE((" physical partitions: %d\n", physicalCount)); 229 TRACE((" virtual partitions: %d\n", virtualCount)); 230 TRACE((" sparable partitions: %d\n", sparableCount)); 231 TRACE((" metadata partitions: %d\n", metadataCount)); 232 } 233 } 234 235 // We're now going to start creating Icb's, which will expect 236 // certain parts of the volume to be initialized properly. Thus, 237 // we initialize those parts here. 238 if (!status) { 239 fDevice = device; 240 fOffset = offset; 241 fLength = length; 242 fBlockSize = blockSize; 243 fBlockShift = blockShift; 244 } 245 TRACE(("Volume::Mount: device = %d, offset = %" B_PRIdOFF ", length = %" 246 B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n", 247 device, offset, length, blockSize, blockShift)); 248 // At this point we've found a valid set of volume descriptors and 249 // our partitions are all set up. We now need to investigate the file 250 // set descriptor pointed to by the logical volume descriptor. 251 if (!status) { 252 TRACE(("Volume::Mount: Partition has been set up\n")); 253 MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length()); 254 255 status = chunk.InitCheck(); 256 257 if (!status) { 258 off_t address; 259 // Read in the file set descriptor 260 status = MapBlock(logicalVolumeDescriptor.file_set_address(), 261 &address); 262 if (!status) 263 address <<= blockShift; 264 if (!status) { 265 ssize_t bytesRead 266 = read_pos(device, address, chunk.Data(), blockSize); 267 if (bytesRead != ssize_t(blockSize)) { 268 status = B_IO_ERROR; 269 TRACE_ERROR(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32 270 ") failed with: 0x%lx\n", address, blockSize, 271 bytesRead)); 272 } 273 } 274 // See if it's valid, and if so, create the root icb 275 if (!status) { 276 file_set_descriptor *fileSet = 277 reinterpret_cast<file_set_descriptor*>(chunk.Data()); 278 PDUMP(fileSet); 279 status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR 280 ? B_OK : B_ERROR; 281 if (!status) 282 status = fileSet->tag().init_check( 283 logicalVolumeDescriptor.file_set_address().block()); 284 if (!status) { 285 PDUMP(fileSet); 286 fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb()); 287 if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK) 288 return B_NO_MEMORY; 289 } 290 291 TRACE(("Volume::Mount: Root Node id = %" B_PRIdINO "\n", 292 fRootIcb->Id())); 293 if (!status) { 294 status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb, 295 &gUDFVnodeOps, fRootIcb->Mode(), 0); 296 if (status != B_OK) { 297 TRACE_ERROR(("Error creating vnode for root icb! " 298 "status = 0x%" B_PRIx32 ", `%s'\n", status, 299 strerror(status))); 300 // Clean up the icb we created, since _Unset() 301 // won't do this for us. 302 delete fRootIcb; 303 fRootIcb = NULL; 304 } 305 TRACE(("Volume::Mount: Root vnode published. Id = %" 306 B_PRIdINO "\n", fRootIcb->Id())); 307 } 308 } 309 } 310 } 311 312 // If we've made it this far, we're good to go; set the volume 313 // name and then flag that we're mounted. On the other hand, if 314 // an error occurred, we need to clean things up. 315 if (!status) { 316 fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier()); 317 fMounted = true; 318 } else { 319 _Unset(); 320 } 321 322 RETURN(status); 323 } 324 325 326 const char* 327 Volume::Name() const { 328 return fName.Utf8(); 329 } 330 331 /*! \brief Maps the given logical block to a physical block. 332 */ 333 status_t 334 Volume::MapBlock(long_address address, off_t *mappedBlock) 335 { 336 TRACE(("Volume::MapBlock: partition = %d, block = %" B_PRIu32 337 ", mappedBlock = %p\n", address.partition(), address.block(), 338 mappedBlock)); 339 DEBUG_INIT_ETC("Volume", ("partition = %d, block = %" B_PRIu32 340 ", mappedBlock = %p", address.partition(), address.block(), 341 mappedBlock)); 342 status_t error = mappedBlock ? B_OK : B_BAD_VALUE; 343 if (!error) { 344 Partition *partition = _GetPartition(address.partition()); 345 error = partition ? B_OK : B_BAD_ADDRESS; 346 if (!error) 347 error = partition->MapBlock(address.block(), *mappedBlock); 348 } 349 RETURN(error); 350 } 351 352 /*! \brief Unsets the volume and deletes any partitions. 353 354 Does *not* delete the root icb object. 355 */ 356 void 357 Volume::_Unset() 358 { 359 DEBUG_INIT("Volume"); 360 // delete our partitions 361 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) 362 _SetPartition(i, NULL); 363 fFSVolume->id = 0; 364 if (fDevice >= 0) { 365 block_cache_delete(fBlockCache, true); 366 close(fDevice); 367 } 368 fBlockCache = NULL; 369 fDevice = -1; 370 fMounted = false; 371 fOffset = 0; 372 fLength = 0; 373 fBlockSize = 0; 374 fBlockShift = 0; 375 fName.SetTo("", 0); 376 } 377 378 /*! \brief Sets the partition associated with the given number after 379 deleting any previously associated partition. 380 381 \param number The partition number (should be the same as the index 382 into the lvd's partition map array). 383 \param partition The new partition (may be NULL). 384 */ 385 status_t 386 Volume::_SetPartition(uint number, Partition *partition) 387 { 388 status_t error = number < UDF_MAX_PARTITION_MAPS 389 ? B_OK : B_BAD_VALUE; 390 if (!error) { 391 delete fPartitions[number]; 392 fPartitions[number] = partition; 393 } 394 return error; 395 } 396 397 /*! \brief Returns the partition associated with the given number, or 398 NULL if no such partition exists or the number is invalid. 399 */ 400 Partition* 401 Volume::_GetPartition(uint number) 402 { 403 return (number < UDF_MAX_PARTITION_MAPS) 404 ? fPartitions[number] : NULL; 405 } 406