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 = %Ld, length = %Ld, " 52 "blockSize: %ld, flags: %ld\n", deviceName, offset, length, blockSize, 53 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: %ld, length: %ld)\n", map->partition_number(), 138 descriptor->start(), descriptor->length())); 139 status = _SetPartition(i, partition); 140 if (!status) 141 physicalCount++; 142 } 143 } else { 144 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n")); 145 status = B_ERROR; 146 } 147 } else if (header->type() == 2) { 148 // Figure out what kind of type 2 partition map we have based 149 // on the type identifier 150 const entity_id &typeId = header->partition_type_id(); 151 DUMP(typeId); 152 DUMP(kSparablePartitionMapId); 153 if (typeId.matches(kVirtualPartitionMapId)) { 154 TRACE(("map type: virtual\n")); 155 virtual_partition_map* map = 156 reinterpret_cast<virtual_partition_map*>(header); 157 virtualCount++; 158 (void)map; // kill the warning for now 159 } else if (typeId.matches(kSparablePartitionMapId)) { 160 TRACE(("map type: sparable\n")); 161 sparable_partition_map* map = 162 reinterpret_cast<sparable_partition_map*>(header); 163 sparableCount++; 164 (void)map; // kill the warning for now 165 } else if (typeId.matches(kMetadataPartitionMapId)) { 166 TRACE(("map type: metadata\n")); 167 metadata_partition_map* map = 168 reinterpret_cast<metadata_partition_map*>(header); 169 170 171 // Find the corresponding partition descriptor 172 partition_descriptor *descriptor = NULL; 173 for (uint8 j = 0; j < partitionDescriptorCount; j++) { 174 if (map->partition_number() == 175 partitionDescriptors[j].partition_number()) { 176 descriptor = &partitionDescriptors[j]; 177 break; 178 } 179 } 180 Partition *parent = _GetPartition(map->partition_number()); 181 // Create and add the partition 182 if (descriptor != NULL && parent != NULL) { 183 MetadataPartition *partition 184 = new(nothrow) MetadataPartition(this, 185 map->partition_number(), *parent, 186 map->metadata_file_location(), 187 map->metadata_mirror_file_location(), 188 map->metadata_bitmap_file_location(), 189 map->allocation_unit_size(), 190 map->alignment_unit_size(), 191 map->flags() & 1); 192 status = partition ? partition->InitCheck() : B_NO_MEMORY; 193 if (!status) { 194 TRACE(("Volume::Mount: adding MetadataPartition()")); 195 status = _SetPartition(i, partition); 196 if (status == B_OK) 197 metadataCount++; 198 } else { 199 TRACE_ERROR(("Volume::Mount: metadata partition " 200 "creation failed! 0x%lx\n", status)); 201 } 202 } else { 203 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n")); 204 status = B_ERROR; 205 } 206 } else { 207 TRACE(("map type: unrecognized (`%.23s')\n", 208 typeId.identifier())); 209 status = B_ERROR; 210 } 211 } else { 212 TRACE(("Invalid partition type %d found!\n", header->type())); 213 status = B_ERROR; 214 } 215 offset += header->length(); 216 } 217 218 // Do some checking as to what sorts of partitions we've actually found. 219 if (!status) { 220 status = (physicalCount == 1 && virtualCount == 0 221 && sparableCount == 0) 222 || (physicalCount == 2 && virtualCount == 0 223 && sparableCount == 0) 224 ? B_OK : B_ERROR; 225 if (status) { 226 TRACE(("Invalid partition layout found:\n")); 227 TRACE((" physical partitions: %d\n", physicalCount)); 228 TRACE((" virtual partitions: %d\n", virtualCount)); 229 TRACE((" sparable partitions: %d\n", sparableCount)); 230 TRACE((" metadata partitions: %d\n", metadataCount)); 231 } 232 } 233 234 // We're now going to start creating Icb's, which will expect 235 // certain parts of the volume to be initialized properly. Thus, 236 // we initialize those parts here. 237 if (!status) { 238 fDevice = device; 239 fOffset = offset; 240 fLength = length; 241 fBlockSize = blockSize; 242 fBlockShift = blockShift; 243 } 244 TRACE(("Volume::Mount: device = %d, offset = %Ld, length = %Ld, " 245 "blockSize = %ld, blockShift = %ld\n", device, offset, length, 246 blockSize, blockShift)); 247 // At this point we've found a valid set of volume descriptors and 248 // our partitions are all set up. We now need to investigate the file 249 // set descriptor pointed to by the logical volume descriptor. 250 if (!status) { 251 TRACE(("Volume::Mount: Partition has been set up\n")); 252 MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length()); 253 254 status = chunk.InitCheck(); 255 256 if (!status) { 257 off_t address; 258 // Read in the file set descriptor 259 status = MapBlock(logicalVolumeDescriptor.file_set_address(), 260 &address); 261 if (!status) 262 address <<= blockShift; 263 if (!status) { 264 ssize_t bytesRead 265 = read_pos(device, address, chunk.Data(), blockSize); 266 if (bytesRead != ssize_t(blockSize)) { 267 status = B_IO_ERROR; 268 TRACE_ERROR(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", 269 address, blockSize, bytesRead)); 270 } 271 } 272 // See if it's valid, and if so, create the root icb 273 if (!status) { 274 file_set_descriptor *fileSet = 275 reinterpret_cast<file_set_descriptor*>(chunk.Data()); 276 PDUMP(fileSet); 277 status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR 278 ? B_OK : B_ERROR; 279 if (!status) 280 status = fileSet->tag().init_check( 281 logicalVolumeDescriptor.file_set_address().block()); 282 if (!status) { 283 PDUMP(fileSet); 284 fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb()); 285 if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK) 286 return B_NO_MEMORY; 287 } 288 289 TRACE(("Volume::Mount: Root Node id = %Ld\n", fRootIcb->Id())); 290 if (!status) { 291 status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb, 292 &gUDFVnodeOps, fRootIcb->Mode(), 0); 293 if (status != B_OK) { 294 TRACE_ERROR(("Error creating vnode for root icb! " 295 "status = 0x%lx, `%s'\n", status, 296 strerror(status))); 297 // Clean up the icb we created, since _Unset() 298 // won't do this for us. 299 delete fRootIcb; 300 fRootIcb = NULL; 301 } 302 TRACE(("Volume::Mount: Root vnode published. Id = %Ld\n", 303 fRootIcb->Id())); 304 } 305 } 306 } 307 } 308 309 // If we've made it this far, we're good to go; set the volume 310 // name and then flag that we're mounted. On the other hand, if 311 // an error occurred, we need to clean things up. 312 if (!status) { 313 fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier()); 314 fMounted = true; 315 } else { 316 _Unset(); 317 } 318 319 RETURN(status); 320 } 321 322 323 const char* 324 Volume::Name() const { 325 return fName.Utf8(); 326 } 327 328 /*! \brief Maps the given logical block to a physical block. 329 */ 330 status_t 331 Volume::MapBlock(long_address address, off_t *mappedBlock) 332 { 333 TRACE(("Volume::MapBlock: partition = %d, block = %ld, mappedBlock = %p\n", 334 address.partition(), address.block(), mappedBlock)); 335 DEBUG_INIT_ETC("Volume", ("partition = %d, block = %ld, mappedBlock = %p", 336 address.partition(), address.block(), mappedBlock)); 337 status_t error = mappedBlock ? B_OK : B_BAD_VALUE; 338 if (!error) { 339 Partition *partition = _GetPartition(address.partition()); 340 error = partition ? B_OK : B_BAD_ADDRESS; 341 if (!error) 342 error = partition->MapBlock(address.block(), *mappedBlock); 343 } 344 RETURN(error); 345 } 346 347 /*! \brief Unsets the volume and deletes any partitions. 348 349 Does *not* delete the root icb object. 350 */ 351 void 352 Volume::_Unset() 353 { 354 DEBUG_INIT("Volume"); 355 // delete our partitions 356 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) 357 _SetPartition(i, NULL); 358 fFSVolume->id = 0; 359 if (fDevice >= 0) { 360 block_cache_delete(fBlockCache, true); 361 close(fDevice); 362 } 363 fBlockCache = NULL; 364 fDevice = -1; 365 fMounted = false; 366 fOffset = 0; 367 fLength = 0; 368 fBlockSize = 0; 369 fBlockShift = 0; 370 fName.SetTo("", 0); 371 } 372 373 /*! \brief Sets the partition associated with the given number after 374 deleting any previously associated partition. 375 376 \param number The partition number (should be the same as the index 377 into the lvd's partition map array). 378 \param partition The new partition (may be NULL). 379 */ 380 status_t 381 Volume::_SetPartition(uint number, Partition *partition) 382 { 383 status_t error = number < UDF_MAX_PARTITION_MAPS 384 ? B_OK : B_BAD_VALUE; 385 if (!error) { 386 delete fPartitions[number]; 387 fPartitions[number] = partition; 388 } 389 return error; 390 } 391 392 /*! \brief Returns the partition associated with the given number, or 393 NULL if no such partition exists or the number is invalid. 394 */ 395 Partition* 396 Volume::_GetPartition(uint number) 397 { 398 return (number < UDF_MAX_PARTITION_MAPS) 399 ? fPartitions[number] : NULL; 400 } 401