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(-1) 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 #if !USER 68 struct stat stat; 69 error = fstat(device, &stat) < 0 ? B_ERROR : B_OK; 70 if (!error) { 71 if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) { 72 DIE(("Unable to disable cache of underlying file system.\n")); 73 } 74 } 75 #endif 76 77 logical_volume_descriptor logicalVolumeDescriptor; 78 partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors]; 79 uint8 partitionDescriptorCount; 80 uint32 blockShift; 81 82 // Run through the volume recognition and descriptor sequences to 83 // see if we have a potentially valid UDF volume on our hands 84 error = udf_recognize(device, offset, length, blockSize, blockShift, 85 logicalVolumeDescriptor, partitionDescriptors, 86 partitionDescriptorCount); 87 88 // Set up the block cache 89 if (!error) 90 error = init_cache_for_device(device, length); 91 92 int physicalCount = 0; 93 int virtualCount = 0; 94 int sparableCount = 0; 95 int metadataCount = 0; 96 97 // Set up the partitions 98 if (!error) { 99 // Set up physical and sparable partitions first 100 int offset = 0; 101 for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count() 102 && !error; i++) 103 { 104 uint8 *maps = logicalVolumeDescriptor.partition_maps(); 105 partition_map_header *header = 106 reinterpret_cast<partition_map_header*>(maps+offset); 107 PRINT(("partition map %d (type %d):\n", i, header->type())); 108 if (header->type() == 1) { 109 PRINT(("map type: physical\n")); 110 physical_partition_map* map = 111 reinterpret_cast<physical_partition_map*>(header); 112 // Find the corresponding partition descriptor 113 partition_descriptor *descriptor = NULL; 114 for (uint8 j = 0; j < partitionDescriptorCount; j++) { 115 if (map->partition_number() == 116 partitionDescriptors[j].partition_number()) 117 { 118 descriptor = &partitionDescriptors[j]; 119 break; 120 } 121 } 122 // Create and add the partition 123 if (descriptor) { 124 PhysicalPartition *partition = new PhysicalPartition( 125 map->partition_number(), 126 descriptor->start(), 127 descriptor->length()); 128 error = partition ? B_OK : B_NO_MEMORY; 129 if (!error) { 130 PRINT(("Adding PhysicalPartition(number: %d, start: %ld, " 131 "length: %ld)\n", map->partition_number(), 132 descriptor->start(), descriptor->length())); 133 error = _SetPartition(i, partition); 134 if (!error) 135 physicalCount++; 136 } 137 } else { 138 PRINT(("no matching partition descriptor found!\n")); 139 error = B_ERROR; 140 } 141 } else if (header->type() == 2) { 142 // Figure out what kind of type 2 partition map we have based 143 // on the type identifier 144 const entity_id &typeId = header->partition_type_id(); 145 DUMP(typeId); 146 DUMP(kSparablePartitionMapId); 147 if (typeId.matches(kVirtualPartitionMapId)) { 148 PRINT(("map type: virtual\n")); 149 virtual_partition_map* map = 150 reinterpret_cast<virtual_partition_map*>(header); 151 virtualCount++; 152 (void)map; // kill the warning for now 153 } else if (typeId.matches(kSparablePartitionMapId)) { 154 PRINT(("map type: sparable\n")); 155 sparable_partition_map* map = 156 reinterpret_cast<sparable_partition_map*>(header); 157 sparableCount++; 158 (void)map; // kill the warning for now 159 } else if (typeId.matches(kMetadataPartitionMapId)) { 160 PRINT(("map type: metadata\n")); 161 metadata_partition_map* map = 162 reinterpret_cast<metadata_partition_map*>(header); 163 metadataCount++; 164 (void)map; // kill the warning for now 165 } else { 166 PRINT(("map type: unrecognized (`%.23s')\n", 167 typeId.identifier())); 168 error = B_ERROR; 169 } 170 } else { 171 PRINT(("Invalid partition type %d found!\n", header->type())); 172 error = B_ERROR; 173 } 174 offset += header->length(); 175 } 176 } 177 178 // Do some checking as to what sorts of partitions we've actually found. 179 if (!error) { 180 error = (physicalCount == 1 && virtualCount == 0 181 && sparableCount == 0 && metadataCount == 0) 182 || (physicalCount == 2 && virtualCount == 0 183 && sparableCount == 0 && metadataCount == 0) 184 ? B_OK : B_ERROR; 185 if (error) { 186 PRINT(("Invalid partition layout found:\n")); 187 PRINT((" physical partitions: %d\n", physicalCount)); 188 PRINT((" virtual partitions: %d\n", virtualCount)); 189 PRINT((" sparable partitions: %d\n", sparableCount)); 190 PRINT((" metadata partitions: %d\n", metadataCount)); 191 } 192 } 193 194 // We're now going to start creating Icb's, which will expect 195 // certain parts of the volume to be initialized properly. Thus, 196 // we initialize those parts here. 197 if (!error) { 198 fDevice = device; 199 fOffset = offset; 200 fLength = length; 201 fBlockSize = blockSize; 202 fBlockShift = blockShift; 203 } 204 205 // At this point we've found a valid set of volume descriptors and 206 // our partitions are all set up. We now need to investigate the file 207 // set descriptor pointed to by the logical volume descriptor. 208 if (!error) { 209 MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length()); 210 211 error = chunk.InitCheck(); 212 213 if (!error) { 214 off_t address; 215 // Read in the file set descriptor 216 error = MapBlock(logicalVolumeDescriptor.file_set_address(), 217 &address); 218 if (!error) 219 address <<= blockShift; 220 if (!error) { 221 ssize_t bytesRead = read_pos(device, address, chunk.Data(), 222 blockSize); 223 if (bytesRead != ssize_t(blockSize)) { 224 error = B_IO_ERROR; 225 PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", 226 address, blockSize, bytesRead)); 227 } 228 } 229 // See if it's valid, and if so, create the root icb 230 if (!error) { 231 file_set_descriptor *fileSet = 232 reinterpret_cast<file_set_descriptor*>(chunk.Data()); 233 PDUMP(fileSet); 234 error = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR 235 ? B_OK : B_ERROR; 236 if (!error) 237 error = fileSet->tag().init_check( 238 logicalVolumeDescriptor.file_set_address().block()); 239 if (!error) { 240 PDUMP(fileSet); 241 fRootIcb = new Icb(this, fileSet->root_directory_icb()); 242 error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY; 243 } 244 if (!error) { 245 error = new_vnode(Id(), RootIcb()->Id(), (void*)RootIcb()); 246 if (error) { 247 PRINT(("Error creating vnode for root icb! " 248 "error = 0x%lx, `%s'\n", error, 249 strerror(error))); 250 // Clean up the icb we created, since _Unset() 251 // won't do this for us. 252 delete fRootIcb; 253 fRootIcb = NULL; 254 } 255 } 256 } 257 } 258 } 259 260 // If we've made it this far, we're good to go; set the volume 261 // name and then flag that we're mounted. On the other hand, if 262 // an error occurred, we need to clean things up. 263 if (!error) { 264 fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier()); 265 fMounted = true; 266 } else { 267 _Unset(); 268 } 269 270 RETURN(error); 271 } 272 273 const char* 274 Volume::Name() const { 275 return fName.Utf8(); 276 } 277 278 /*! \brief Maps the given logical block to a physical block. 279 */ 280 status_t 281 Volume::MapBlock(long_address address, off_t *mappedBlock) 282 { 283 DEBUG_INIT_ETC("Volume", 284 ("partition: %d, block: %ld, mappedBlock: %p", 285 address.partition(), address.block(), mappedBlock)); 286 status_t error = mappedBlock ? B_OK : B_BAD_VALUE; 287 if (!error) { 288 Partition *partition = _GetPartition(address.partition()); 289 error = partition ? B_OK : B_BAD_ADDRESS; 290 if (!error) 291 error = partition->MapBlock(address.block(), *mappedBlock); 292 } 293 RETURN(error); 294 } 295 296 /*! \brief Unsets the volume and deletes any partitions. 297 298 Does *not* delete the root icb object. 299 */ 300 void 301 Volume::_Unset() 302 { 303 DEBUG_INIT("Volume"); 304 fId = 0; 305 if (fDevice >= 0) 306 close(fDevice); 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 348