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