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