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