xref: /haiku/src/add-ons/kernel/file_systems/udf/Volume.cpp (revision 9b8775ea747eb9c5a5879a45754f5311fee8225a)
129008bcfSTyler Dauwalder //----------------------------------------------------------------------
229008bcfSTyler Dauwalder //  This software is part of the OpenBeOS distribution and is covered
329008bcfSTyler Dauwalder //  by the OpenBeOS license.
429008bcfSTyler Dauwalder //
529008bcfSTyler Dauwalder //  Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
629008bcfSTyler Dauwalder //  Mad props to Axel Dörfler and his BFS implementation, from which
729008bcfSTyler Dauwalder //  this UDF implementation draws much influence (and a little code :-P).
829008bcfSTyler Dauwalder //----------------------------------------------------------------------
929008bcfSTyler Dauwalder #include "Volume.h"
1029008bcfSTyler Dauwalder 
113f4628f1STyler Dauwalder #include "Icb.h"
120d383564STyler Dauwalder #include "MemoryChunk.h"
13d4e8b936STyler Dauwalder #include "PhysicalPartition.h"
1421c162a3STyler Dauwalder #include "Recognition.h"
1529008bcfSTyler Dauwalder 
163f4628f1STyler Dauwalder using namespace Udf;
1729008bcfSTyler Dauwalder 
1829008bcfSTyler Dauwalder /*! \brief Creates an unmounted volume with the given id.
1929008bcfSTyler Dauwalder */
2029008bcfSTyler Dauwalder Volume::Volume(nspace_id id)
213f4628f1STyler Dauwalder 	: fId(id)
2229008bcfSTyler Dauwalder 	, fDevice(0)
2321c162a3STyler Dauwalder 	, fMounted(false)
24bb182cf3STyler Dauwalder 	, fOffset(0)
25d4e8b936STyler Dauwalder 	, fLength(0)
264ac73018STyler Dauwalder 	, fBlockSize(0)
27a71fd512STyler Dauwalder 	, fBlockShift(0)
283f4628f1STyler Dauwalder 	, fRootIcb(NULL)
2929008bcfSTyler Dauwalder {
3021c162a3STyler Dauwalder 	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
3121c162a3STyler Dauwalder 		fPartitions[i] = NULL;
3229008bcfSTyler Dauwalder }
3329008bcfSTyler Dauwalder 
34d4e8b936STyler Dauwalder Volume::~Volume()
3529008bcfSTyler Dauwalder {
36d4e8b936STyler Dauwalder 	_Unset();
3729008bcfSTyler Dauwalder }
3829008bcfSTyler Dauwalder 
3929008bcfSTyler Dauwalder /*! \brief Attempts to mount the given device.
403f4628f1STyler Dauwalder 
413f4628f1STyler Dauwalder 	\param volumeStart The block on the given device whereat the volume begins.
423f4628f1STyler Dauwalder 	\param volumeLength The block length of the volume on the given device.
4329008bcfSTyler Dauwalder */
4429008bcfSTyler Dauwalder status_t
45d4e8b936STyler Dauwalder Volume::Mount(const char *deviceName, off_t offset, off_t length,
4621c162a3STyler Dauwalder               uint32 blockSize, uint32 flags)
4721c162a3STyler Dauwalder {
4821c162a3STyler Dauwalder 	DEBUG_INIT_ETC(CF_PUBLIC | CF_VOLUME_OPS, "Volume",
4921c162a3STyler Dauwalder 	               ("deviceName: `%s', offset: %Ld, length: %Ld, blockSize: %ld, "
5021c162a3STyler Dauwalder                    "flags: %ld", deviceName, offset, length, blockSize, flags));
5121c162a3STyler Dauwalder 	if (!deviceName)
5221c162a3STyler Dauwalder 		RETURN(B_BAD_VALUE);
5321c162a3STyler Dauwalder 	if (Mounted()) {
5421c162a3STyler Dauwalder 		// Already mounted, thank you for asking
5521c162a3STyler Dauwalder 		RETURN(B_BUSY);
5621c162a3STyler Dauwalder 	}
5721c162a3STyler Dauwalder 
5821c162a3STyler Dauwalder 	// Open the device read only
5921c162a3STyler Dauwalder 	int device = open(deviceName, O_RDONLY);
6021c162a3STyler Dauwalder 	if (device < B_OK)
6121c162a3STyler Dauwalder 		RETURN(device);
6221c162a3STyler Dauwalder 
6321c162a3STyler Dauwalder 	status_t error = B_OK;
6421c162a3STyler Dauwalder 
6521c162a3STyler Dauwalder 	// If the device is actually a normal file, try to disable the cache
6621c162a3STyler Dauwalder 	// for the file in the parent filesystem
6721c162a3STyler Dauwalder 	struct stat stat;
6821c162a3STyler Dauwalder 	error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
6921c162a3STyler Dauwalder 	if (!error) {
7021c162a3STyler Dauwalder 		if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
7121c162a3STyler Dauwalder 			DIE(("Unable to disable cache of underlying file system.\n"));
7221c162a3STyler Dauwalder 		}
7321c162a3STyler Dauwalder 	}
7421c162a3STyler Dauwalder 
75*9b8775eaSTyler Dauwalder 	logical_volume_descriptor logicalVolumeDescriptor;
761379cacaSTyler Dauwalder 	partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors];
7721c162a3STyler Dauwalder 	uint8 partitionDescriptorCount;
7821c162a3STyler Dauwalder 	uint32 blockShift;
7921c162a3STyler Dauwalder 
801379cacaSTyler Dauwalder 	// Run through the volume recognition and descriptor sequences to
811379cacaSTyler Dauwalder 	// see if we have a potentially valid UDF volume on our hands
8221c162a3STyler Dauwalder 	error = udf_recognize(device, offset, length, blockSize, blockShift,
8321c162a3STyler Dauwalder 	                               logicalVolumeDescriptor, partitionDescriptors,
8421c162a3STyler Dauwalder 	                               partitionDescriptorCount);
8521c162a3STyler Dauwalder 
8621c162a3STyler Dauwalder 	// Set up the block cache
8721c162a3STyler Dauwalder 	if (!error)
8821c162a3STyler Dauwalder 		error = init_cache_for_device(device, length);
8921c162a3STyler Dauwalder 
90d4e8b936STyler Dauwalder 	int physicalCount = 0;
91d4e8b936STyler Dauwalder 	int virtualCount = 0;
92d4e8b936STyler Dauwalder 	int sparableCount = 0;
93d4e8b936STyler Dauwalder 	int metadataCount = 0;
94d4e8b936STyler Dauwalder 
9521c162a3STyler Dauwalder 	// Set up the partitions
9621c162a3STyler Dauwalder 	if (!error) {
9721c162a3STyler Dauwalder 		// Set up physical and sparable partitions first
9821c162a3STyler Dauwalder 		int offset = 0;
99d4e8b936STyler Dauwalder 		for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count()
100d4e8b936STyler Dauwalder 		     && !error; i++)
101d4e8b936STyler Dauwalder 		{
10221c162a3STyler Dauwalder 			uint8 *maps = logicalVolumeDescriptor.partition_maps();
1031379cacaSTyler Dauwalder 			partition_map_header *header =
1041379cacaSTyler Dauwalder 				reinterpret_cast<partition_map_header*>(maps+offset);
10521c162a3STyler Dauwalder 			PRINT(("partition map %d (type %d):\n", i, header->type()));
10621c162a3STyler Dauwalder 			if (header->type() == 1) {
107d4e8b936STyler Dauwalder 				PRINT(("map type: physical\n"));
1081379cacaSTyler Dauwalder 				physical_partition_map* map =
1091379cacaSTyler Dauwalder 					reinterpret_cast<physical_partition_map*>(header);
110d4e8b936STyler Dauwalder 				// Find the corresponding partition descriptor
1111379cacaSTyler Dauwalder 				partition_descriptor *descriptor = NULL;
112d4e8b936STyler Dauwalder 				for (uint8 j = 0; j < partitionDescriptorCount; j++) {
113d4e8b936STyler Dauwalder 					if (map->partition_number() ==
114d4e8b936STyler Dauwalder 					    partitionDescriptors[j].partition_number())
115d4e8b936STyler Dauwalder 					{
116d4e8b936STyler Dauwalder 						descriptor = &partitionDescriptors[j];
117d4e8b936STyler Dauwalder 						break;
118d4e8b936STyler Dauwalder 					}
119d4e8b936STyler Dauwalder 				}
120d4e8b936STyler Dauwalder 				// Create and add the partition
121d4e8b936STyler Dauwalder 				if (descriptor) {
122d4e8b936STyler Dauwalder 					PhysicalPartition *partition = new PhysicalPartition(
123d4e8b936STyler Dauwalder 					                               map->partition_number(),
124d4e8b936STyler Dauwalder 					                               descriptor->start(),
125d4e8b936STyler Dauwalder 					                               descriptor->length());
126d4e8b936STyler Dauwalder 					error = partition ? B_OK : B_NO_MEMORY;
127d4e8b936STyler Dauwalder 					if (!error) {
128d4e8b936STyler Dauwalder 						PRINT(("Adding PhysicalPartition(number: %d, start: %ld, "
129d4e8b936STyler Dauwalder 						       "length: %ld)\n", map->partition_number(),
130d4e8b936STyler Dauwalder 						       descriptor->start(), descriptor->length()));
131d4e8b936STyler Dauwalder 						error = _SetPartition(i, partition);
132d4e8b936STyler Dauwalder 						if (!error)
133d4e8b936STyler Dauwalder 							physicalCount++;
134d4e8b936STyler Dauwalder 					}
13521c162a3STyler Dauwalder 				} else {
136d4e8b936STyler Dauwalder 					PRINT(("no matching partition descriptor found!\n"));
137d4e8b936STyler Dauwalder 					error = B_ERROR;
138d4e8b936STyler Dauwalder 				}
139d4e8b936STyler Dauwalder 			} else if (header->type() == 2) {
1401379cacaSTyler Dauwalder 				// Figure out what kind of type 2 partition map we have based
141d4e8b936STyler Dauwalder 				// on the type identifier
1421379cacaSTyler Dauwalder 				const entity_id &typeId = header->partition_type_id();
143d4e8b936STyler Dauwalder 				DUMP(typeId);
144d4e8b936STyler Dauwalder 				DUMP(kSparablePartitionMapId);
145d4e8b936STyler Dauwalder 				if (typeId.matches(kVirtualPartitionMapId)) {
146d4e8b936STyler Dauwalder 					PRINT(("map type: virtual\n"));
1471379cacaSTyler Dauwalder 					virtual_partition_map* map =
1481379cacaSTyler Dauwalder 						reinterpret_cast<virtual_partition_map*>(header);
149d4e8b936STyler Dauwalder 					virtualCount++;
150d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
151d4e8b936STyler Dauwalder 				} else if (typeId.matches(kSparablePartitionMapId)) {
152d4e8b936STyler Dauwalder 					PRINT(("map type: sparable\n"));
1531379cacaSTyler Dauwalder 					sparable_partition_map* map =
1541379cacaSTyler Dauwalder 						reinterpret_cast<sparable_partition_map*>(header);
155d4e8b936STyler Dauwalder 					sparableCount++;
156d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
157d4e8b936STyler Dauwalder 				} else if (typeId.matches(kMetadataPartitionMapId)) {
158d4e8b936STyler Dauwalder 					PRINT(("map type: metadata\n"));
1591379cacaSTyler Dauwalder 					metadata_partition_map* map =
1601379cacaSTyler Dauwalder 						reinterpret_cast<metadata_partition_map*>(header);
161d4e8b936STyler Dauwalder 					metadataCount++;
162d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
163d4e8b936STyler Dauwalder 				} else {
164d4e8b936STyler Dauwalder 					PRINT(("map type: unrecognized (`%.23s')\n",
165d4e8b936STyler Dauwalder 					       typeId.identifier()));
166d4e8b936STyler Dauwalder 					error = B_ERROR;
16721c162a3STyler Dauwalder 				}
168d4e8b936STyler Dauwalder 			} else {
169d4e8b936STyler Dauwalder 				PRINT(("Invalid partition type %d found!\n", header->type()));
170d4e8b936STyler Dauwalder 				error = B_ERROR;
171d4e8b936STyler Dauwalder 			}
17221c162a3STyler Dauwalder 			offset += header->length();
17321c162a3STyler Dauwalder 		}
17421c162a3STyler Dauwalder 	}
17521c162a3STyler Dauwalder 
176d4e8b936STyler Dauwalder 	// Do some checking as to what sorts of partitions we've actually found.
17721c162a3STyler Dauwalder 	if (!error) {
178d4e8b936STyler Dauwalder 		error = (physicalCount == 1 && virtualCount == 0
179d4e8b936STyler Dauwalder 		         && sparableCount == 0 && metadataCount == 0)
180d4e8b936STyler Dauwalder 		        || (physicalCount == 2 && virtualCount == 0
181d4e8b936STyler Dauwalder 		           && sparableCount == 0 && metadataCount == 0)
182d4e8b936STyler Dauwalder 		        ? B_OK : B_ERROR;
183d4e8b936STyler Dauwalder 		if (error) {
184d4e8b936STyler Dauwalder 			PRINT(("Invalid partition layout found:\n"));
185d4e8b936STyler Dauwalder 			PRINT(("  physical partitions: %d\n", physicalCount));
186d4e8b936STyler Dauwalder 			PRINT(("  virtual partitions:  %d\n", virtualCount));
187d4e8b936STyler Dauwalder 			PRINT(("  sparable partitions: %d\n", sparableCount));
188d4e8b936STyler Dauwalder 			PRINT(("  metadata partitions: %d\n", metadataCount));
189d4e8b936STyler Dauwalder 		}
190d4e8b936STyler Dauwalder 	}
191d4e8b936STyler Dauwalder 
192d4e8b936STyler Dauwalder 	// We're now going to start creating Icb's, which will expect
193d4e8b936STyler Dauwalder 	// certain parts of the volume to be initialized properly. Thus,
194d4e8b936STyler Dauwalder 	// we initialize those parts here.
195d4e8b936STyler Dauwalder 	if (!error) {
196d4e8b936STyler Dauwalder 		fDevice = device;
197d4e8b936STyler Dauwalder 		fOffset = offset;
198d4e8b936STyler Dauwalder 		fLength = length;
199d4e8b936STyler Dauwalder 		fBlockSize = blockSize;
200d4e8b936STyler Dauwalder 		fBlockShift = blockShift;
201d4e8b936STyler Dauwalder 	}
202d4e8b936STyler Dauwalder 
203d4e8b936STyler Dauwalder 	// At this point we've found a valid set of volume descriptors and
204d4e8b936STyler Dauwalder 	// our partitions are all set up. We now need to investigate the file
205d4e8b936STyler Dauwalder 	// set descriptor pointed to by the logical volume descriptor.
206d4e8b936STyler Dauwalder 	if (!error) {
207d4e8b936STyler Dauwalder 		MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
20821c162a3STyler Dauwalder 
20921c162a3STyler Dauwalder 		status_t error = chunk.InitCheck();
21021c162a3STyler Dauwalder 
21121c162a3STyler Dauwalder 		if (!error) {
212d4e8b936STyler Dauwalder 			off_t address;
213d4e8b936STyler Dauwalder 			// Read in the file set descriptor
214d4e8b936STyler Dauwalder 			error = MapBlock(logicalVolumeDescriptor.file_set_address(),
215d4e8b936STyler Dauwalder 			 	             &address);
216d4e8b936STyler Dauwalder 			if (!error)
217d4e8b936STyler Dauwalder 				address <<= blockShift;
218d4e8b936STyler Dauwalder 			if (!error) {
219d4e8b936STyler Dauwalder 				ssize_t bytesRead = read_pos(device, address, chunk.Data(),
220d4e8b936STyler Dauwalder 				                             blockSize);
221d4e8b936STyler Dauwalder 				if (bytesRead != (ssize_t)blockSize) {
222d4e8b936STyler Dauwalder 					error = B_IO_ERROR;
223d4e8b936STyler Dauwalder 					PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n",
224d4e8b936STyler Dauwalder 					       address, blockSize, bytesRead));
225d4e8b936STyler Dauwalder 				}
226d4e8b936STyler Dauwalder 			}
227d4e8b936STyler Dauwalder 			// See if it's valid, and if so, create the root icb
22821c162a3STyler Dauwalder 			if (!error) {
2291379cacaSTyler Dauwalder 				file_set_descriptor *fileSet =
2301379cacaSTyler Dauwalder 				 	reinterpret_cast<file_set_descriptor*>(chunk.Data());
23121c162a3STyler Dauwalder 				fileSet->tag().init_check(0);
23221c162a3STyler Dauwalder 				PDUMP(fileSet);
23321c162a3STyler Dauwalder 				fRootIcb = new Icb(this, fileSet->root_directory_icb());
23421c162a3STyler Dauwalder 				error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY;
23521c162a3STyler Dauwalder 				if (!error) {
23621c162a3STyler Dauwalder 					error = new_vnode(Id(), RootIcb()->Id(), (void*)RootIcb());
23721c162a3STyler Dauwalder 					if (error) {
238d4e8b936STyler Dauwalder 						PRINT(("Error creating vnode for root icb! "
239d4e8b936STyler Dauwalder 						       "error = 0x%lx, `%s'\n", error,
240d4e8b936STyler Dauwalder 						       strerror(error)));
241d4e8b936STyler Dauwalder 						// Clean up the icb we created, since _Unset()
242d4e8b936STyler Dauwalder 						// won't do this for us.
243d4e8b936STyler Dauwalder 						delete fRootIcb;
244d4e8b936STyler Dauwalder 						fRootIcb = NULL;
245d4e8b936STyler Dauwalder 					}
246d4e8b936STyler Dauwalder 				}
247d4e8b936STyler Dauwalder 			}
24821c162a3STyler Dauwalder 		}
24921c162a3STyler Dauwalder 	}
25021c162a3STyler Dauwalder 
251d4e8b936STyler Dauwalder 	// If we've made it this far, we're good to go; set the volume
252d4e8b936STyler Dauwalder 	// name and then flag that we're mounted. On the other hand, if
253d4e8b936STyler Dauwalder 	// an error occurred, we need to clean things up.
25421c162a3STyler Dauwalder 	if (!error) {
255d4e8b936STyler Dauwalder 		fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
256d4e8b936STyler Dauwalder 		fMounted = true;
257d4e8b936STyler Dauwalder 	} else {
258d4e8b936STyler Dauwalder 		_Unset();
25921c162a3STyler Dauwalder 	}
26021c162a3STyler Dauwalder 
26121c162a3STyler Dauwalder 	RETURN(error);
2624ac73018STyler Dauwalder }
2634ac73018STyler Dauwalder 
264a71fd512STyler Dauwalder const char*
265a71fd512STyler Dauwalder Volume::Name() const {
266a71fd512STyler Dauwalder 	return fName.String();
267a71fd512STyler Dauwalder }
2683f4628f1STyler Dauwalder 
269d4e8b936STyler Dauwalder /*! \brief Maps the given logical block to a physical block.
2703f4628f1STyler Dauwalder */
2713f4628f1STyler Dauwalder status_t
2721379cacaSTyler Dauwalder Volume::MapBlock(long_address address, off_t *mappedBlock)
2730d383564STyler Dauwalder {
2741379cacaSTyler Dauwalder 	DEBUG_INIT_ETC(CF_PRIVATE | CF_HIGH_VOLUME, "Volume",
2751379cacaSTyler Dauwalder 		           ("partition: %d, block: %ld, mappedBlock: %p",
2761379cacaSTyler Dauwalder 		           address.partition(), address.block(), mappedBlock));
277d4e8b936STyler Dauwalder 	status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
278d4e8b936STyler Dauwalder 	if (!error) {
279d4e8b936STyler Dauwalder 		Partition *partition = _GetPartition(address.partition());
280d4e8b936STyler Dauwalder 		error = partition ? B_OK : B_BAD_ADDRESS;
281d4e8b936STyler Dauwalder 		if (!error)
282d4e8b936STyler Dauwalder 			error = partition->MapBlock(address.block(), *mappedBlock);
2833f4628f1STyler Dauwalder 	}
284d4e8b936STyler Dauwalder 	RETURN(error);
2853f4628f1STyler Dauwalder }
2863f4628f1STyler Dauwalder 
287d4e8b936STyler Dauwalder /*! \brief Unsets the volume and deletes any partitions.
288d4e8b936STyler Dauwalder 
289d4e8b936STyler Dauwalder 	Does *not* delete the root icb object.
290d4e8b936STyler Dauwalder */
291d4e8b936STyler Dauwalder void
292d4e8b936STyler Dauwalder Volume::_Unset()
293d4e8b936STyler Dauwalder {
294d4e8b936STyler Dauwalder 	fId = 0;
295d4e8b936STyler Dauwalder 	fDevice = 0;
296d4e8b936STyler Dauwalder 	fMounted = false;
297d4e8b936STyler Dauwalder 	fOffset = 0;
298d4e8b936STyler Dauwalder 	fLength = 0;
299d4e8b936STyler Dauwalder 	fBlockSize = 0;
300d4e8b936STyler Dauwalder 	fBlockShift = 0;
301d4e8b936STyler Dauwalder 	fName.SetTo("");
302d4e8b936STyler Dauwalder 	// delete our partitions
303d4e8b936STyler Dauwalder 	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
304d4e8b936STyler Dauwalder 		_SetPartition(i, NULL);
305d4e8b936STyler Dauwalder }
306d4e8b936STyler Dauwalder 
307d4e8b936STyler Dauwalder /*! \brief Sets the partition associated with the given number after
308d4e8b936STyler Dauwalder 	deleting any previously associated partition.
309d4e8b936STyler Dauwalder 
310d4e8b936STyler Dauwalder 	\param number The partition number (should be the same as the index
311d4e8b936STyler Dauwalder 	              into the lvd's partition map array).
312d4e8b936STyler Dauwalder 	\param partition The new partition (may be NULL).
3133f4628f1STyler Dauwalder */
3143f4628f1STyler Dauwalder status_t
315d4e8b936STyler Dauwalder Volume::_SetPartition(uint number, Partition *partition)
3163f4628f1STyler Dauwalder {
317d4e8b936STyler Dauwalder 	status_t error = number < UDF_MAX_PARTITION_MAPS
318d4e8b936STyler Dauwalder 	                 ? B_OK : B_BAD_VALUE;
319d4e8b936STyler Dauwalder 	if (!error) {
320d4e8b936STyler Dauwalder 		delete fPartitions[number];
321d4e8b936STyler Dauwalder 		fPartitions[number] = partition;
3223f4628f1STyler Dauwalder 	}
323d4e8b936STyler Dauwalder 	return error;
3240d383564STyler Dauwalder }
3250d383564STyler Dauwalder 
326d4e8b936STyler Dauwalder /*! \brief Returns the partition associated with the given number, or
327d4e8b936STyler Dauwalder 	NULL if no such partition exists or the number is invalid.
3283f4628f1STyler Dauwalder */
329d4e8b936STyler Dauwalder Partition*
330d4e8b936STyler Dauwalder Volume::_GetPartition(uint number)
3313f4628f1STyler Dauwalder {
332d4e8b936STyler Dauwalder 	return (number < UDF_MAX_PARTITION_MAPS)
333d4e8b936STyler Dauwalder 	       ? fPartitions[number] : NULL;
3343f4628f1STyler Dauwalder }
3353f4628f1STyler Dauwalder 
336