1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 // 5 // Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net 6 // Mad props to Axel Dörfler and his BFS implementation, from which 7 // this UDF implementation draws much influence (and a little code :-P). 8 //---------------------------------------------------------------------- 9 #include "Volume.h" 10 11 #include "Icb.h" 12 #include "MemoryChunk.h" 13 #include "PhysicalPartition.h" 14 #include "Recognition.h" 15 16 using namespace Udf; 17 18 /*! \brief Creates an unmounted volume with the given id. 19 */ 20 Volume::Volume(nspace_id id) 21 : fId(id) 22 , fDevice(0) 23 , fMounted(false) 24 , fOffset(0) 25 , fLength(0) 26 , fBlockSize(0) 27 , fBlockShift(0) 28 , fRootIcb(NULL) 29 { 30 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) 31 fPartitions[i] = NULL; 32 } 33 34 Volume::~Volume() 35 { 36 _Unset(); 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 DEBUG_INIT_ETC("Volume", 49 ("deviceName: `%s', offset: %Ld, length: %Ld, blockSize: %ld, " 50 "flags: %ld", deviceName, offset, length, blockSize, 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 58 // Open the device read only 59 int device = open(deviceName, O_RDONLY); 60 if (device < B_OK) 61 RETURN(device); 62 63 status_t error = B_OK; 64 65 // If the device is actually a normal file, try to disable the cache 66 // for the file in the parent filesystem 67 struct stat stat; 68 error = fstat(device, &stat) < 0 ? B_ERROR : B_OK; 69 if (!error) { 70 if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) { 71 DIE(("Unable to disable cache of underlying file system.\n")); 72 } 73 } 74 75 logical_volume_descriptor logicalVolumeDescriptor; 76 partition_descriptor partitionDescriptors[Udf::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, length, blockSize, blockShift, 83 logicalVolumeDescriptor, partitionDescriptors, 84 partitionDescriptorCount); 85 86 // Set up the block cache 87 if (!error) 88 error = init_cache_for_device(device, length); 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 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 "length: %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 = length; 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 status_t 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 fileSet->tag().init_check(0); 232 PDUMP(fileSet); 233 fRootIcb = new Icb(this, fileSet->root_directory_icb()); 234 error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY; 235 if (!error) { 236 error = new_vnode(Id(), RootIcb()->Id(), (void*)RootIcb()); 237 if (error) { 238 PRINT(("Error creating vnode for root icb! " 239 "error = 0x%lx, `%s'\n", error, 240 strerror(error))); 241 // Clean up the icb we created, since _Unset() 242 // won't do this for us. 243 delete fRootIcb; 244 fRootIcb = NULL; 245 } 246 } 247 } 248 } 249 } 250 251 // If we've made it this far, we're good to go; set the volume 252 // name and then flag that we're mounted. On the other hand, if 253 // an error occurred, we need to clean things up. 254 if (!error) { 255 fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier()); 256 fMounted = true; 257 } else { 258 _Unset(); 259 } 260 261 RETURN(error); 262 } 263 264 const char* 265 Volume::Name() const { 266 return fName.Utf8(); 267 } 268 269 /*! \brief Maps the given logical block to a physical block. 270 */ 271 status_t 272 Volume::MapBlock(long_address address, off_t *mappedBlock) 273 { 274 DEBUG_INIT_ETC("Volume", 275 ("partition: %d, block: %ld, mappedBlock: %p", 276 address.partition(), address.block(), mappedBlock)); 277 status_t error = mappedBlock ? B_OK : B_BAD_VALUE; 278 if (!error) { 279 Partition *partition = _GetPartition(address.partition()); 280 error = partition ? B_OK : B_BAD_ADDRESS; 281 if (!error) 282 error = partition->MapBlock(address.block(), *mappedBlock); 283 } 284 RETURN(error); 285 } 286 287 /*! \brief Unsets the volume and deletes any partitions. 288 289 Does *not* delete the root icb object. 290 */ 291 void 292 Volume::_Unset() 293 { 294 fId = 0; 295 fDevice = 0; 296 fMounted = false; 297 fOffset = 0; 298 fLength = 0; 299 fBlockSize = 0; 300 fBlockShift = 0; 301 fName.SetTo("", 0); 302 // delete our partitions 303 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) 304 _SetPartition(i, NULL); 305 } 306 307 /*! \brief Sets the partition associated with the given number after 308 deleting any previously associated partition. 309 310 \param number The partition number (should be the same as the index 311 into the lvd's partition map array). 312 \param partition The new partition (may be NULL). 313 */ 314 status_t 315 Volume::_SetPartition(uint number, Partition *partition) 316 { 317 status_t error = number < UDF_MAX_PARTITION_MAPS 318 ? B_OK : B_BAD_VALUE; 319 if (!error) { 320 delete fPartitions[number]; 321 fPartitions[number] = partition; 322 } 323 return error; 324 } 325 326 /*! \brief Returns the partition associated with the given number, or 327 NULL if no such partition exists or the number is invalid. 328 */ 329 Partition* 330 Volume::_GetPartition(uint number) 331 { 332 return (number < UDF_MAX_PARTITION_MAPS) 333 ? fPartitions[number] : NULL; 334 } 335 336