xref: /haiku/src/add-ons/kernel/file_systems/udf/Volume.cpp (revision e376a854f69878384bca8e6e8de2782924232b06)
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)
22237ced11STyler Dauwalder 	, fDevice(-1)
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 {
4821ea9aeaSTyler Dauwalder 	DEBUG_INIT_ETC("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
67*e376a854STyler Dauwalder #if !USER
6821c162a3STyler Dauwalder 	struct stat stat;
6921c162a3STyler Dauwalder 	error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
7021c162a3STyler Dauwalder 	if (!error) {
7121c162a3STyler Dauwalder 		if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
7221c162a3STyler Dauwalder 			DIE(("Unable to disable cache of underlying file system.\n"));
7321c162a3STyler Dauwalder 		}
7421c162a3STyler Dauwalder 	}
75*e376a854STyler Dauwalder #endif
7621c162a3STyler Dauwalder 
779b8775eaSTyler Dauwalder 	logical_volume_descriptor logicalVolumeDescriptor;
781379cacaSTyler Dauwalder 	partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors];
7921c162a3STyler Dauwalder 	uint8 partitionDescriptorCount;
8021c162a3STyler Dauwalder 	uint32 blockShift;
8121c162a3STyler Dauwalder 
821379cacaSTyler Dauwalder 	// Run through the volume recognition and descriptor sequences to
831379cacaSTyler Dauwalder 	// see if we have a potentially valid UDF volume on our hands
8421c162a3STyler Dauwalder 	error = udf_recognize(device, offset, length, blockSize, blockShift,
8521c162a3STyler Dauwalder 	                               logicalVolumeDescriptor, partitionDescriptors,
8621c162a3STyler Dauwalder 	                               partitionDescriptorCount);
8721c162a3STyler Dauwalder 
8821c162a3STyler Dauwalder 	// Set up the block cache
8921c162a3STyler Dauwalder 	if (!error)
9021c162a3STyler Dauwalder 		error = init_cache_for_device(device, length);
9121c162a3STyler Dauwalder 
92d4e8b936STyler Dauwalder 	int physicalCount = 0;
93d4e8b936STyler Dauwalder 	int virtualCount = 0;
94d4e8b936STyler Dauwalder 	int sparableCount = 0;
95d4e8b936STyler Dauwalder 	int metadataCount = 0;
96d4e8b936STyler Dauwalder 
9721c162a3STyler Dauwalder 	// Set up the partitions
9821c162a3STyler Dauwalder 	if (!error) {
9921c162a3STyler Dauwalder 		// Set up physical and sparable partitions first
10021c162a3STyler Dauwalder 		int offset = 0;
101d4e8b936STyler Dauwalder 		for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count()
102d4e8b936STyler Dauwalder 		     && !error; i++)
103d4e8b936STyler Dauwalder 		{
10421c162a3STyler Dauwalder 			uint8 *maps = logicalVolumeDescriptor.partition_maps();
1051379cacaSTyler Dauwalder 			partition_map_header *header =
1061379cacaSTyler Dauwalder 				reinterpret_cast<partition_map_header*>(maps+offset);
10721c162a3STyler Dauwalder 			PRINT(("partition map %d (type %d):\n", i, header->type()));
10821c162a3STyler Dauwalder 			if (header->type() == 1) {
109d4e8b936STyler Dauwalder 				PRINT(("map type: physical\n"));
1101379cacaSTyler Dauwalder 				physical_partition_map* map =
1111379cacaSTyler Dauwalder 					reinterpret_cast<physical_partition_map*>(header);
112d4e8b936STyler Dauwalder 				// Find the corresponding partition descriptor
1131379cacaSTyler Dauwalder 				partition_descriptor *descriptor = NULL;
114d4e8b936STyler Dauwalder 				for (uint8 j = 0; j < partitionDescriptorCount; j++) {
115d4e8b936STyler Dauwalder 					if (map->partition_number() ==
116d4e8b936STyler Dauwalder 					    partitionDescriptors[j].partition_number())
117d4e8b936STyler Dauwalder 					{
118d4e8b936STyler Dauwalder 						descriptor = &partitionDescriptors[j];
119d4e8b936STyler Dauwalder 						break;
120d4e8b936STyler Dauwalder 					}
121d4e8b936STyler Dauwalder 				}
122d4e8b936STyler Dauwalder 				// Create and add the partition
123d4e8b936STyler Dauwalder 				if (descriptor) {
124d4e8b936STyler Dauwalder 					PhysicalPartition *partition = new PhysicalPartition(
125d4e8b936STyler Dauwalder 					                               map->partition_number(),
126d4e8b936STyler Dauwalder 					                               descriptor->start(),
127d4e8b936STyler Dauwalder 					                               descriptor->length());
128d4e8b936STyler Dauwalder 					error = partition ? B_OK : B_NO_MEMORY;
129d4e8b936STyler Dauwalder 					if (!error) {
130d4e8b936STyler Dauwalder 						PRINT(("Adding PhysicalPartition(number: %d, start: %ld, "
131d4e8b936STyler Dauwalder 						       "length: %ld)\n", map->partition_number(),
132d4e8b936STyler Dauwalder 						       descriptor->start(), descriptor->length()));
133d4e8b936STyler Dauwalder 						error = _SetPartition(i, partition);
134d4e8b936STyler Dauwalder 						if (!error)
135d4e8b936STyler Dauwalder 							physicalCount++;
136d4e8b936STyler Dauwalder 					}
13721c162a3STyler Dauwalder 				} else {
138d4e8b936STyler Dauwalder 					PRINT(("no matching partition descriptor found!\n"));
139d4e8b936STyler Dauwalder 					error = B_ERROR;
140d4e8b936STyler Dauwalder 				}
141d4e8b936STyler Dauwalder 			} else if (header->type() == 2) {
1421379cacaSTyler Dauwalder 				// Figure out what kind of type 2 partition map we have based
143d4e8b936STyler Dauwalder 				// on the type identifier
1441379cacaSTyler Dauwalder 				const entity_id &typeId = header->partition_type_id();
145d4e8b936STyler Dauwalder 				DUMP(typeId);
146d4e8b936STyler Dauwalder 				DUMP(kSparablePartitionMapId);
147d4e8b936STyler Dauwalder 				if (typeId.matches(kVirtualPartitionMapId)) {
148d4e8b936STyler Dauwalder 					PRINT(("map type: virtual\n"));
1491379cacaSTyler Dauwalder 					virtual_partition_map* map =
1501379cacaSTyler Dauwalder 						reinterpret_cast<virtual_partition_map*>(header);
151d4e8b936STyler Dauwalder 					virtualCount++;
152d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
153d4e8b936STyler Dauwalder 				} else if (typeId.matches(kSparablePartitionMapId)) {
154d4e8b936STyler Dauwalder 					PRINT(("map type: sparable\n"));
1551379cacaSTyler Dauwalder 					sparable_partition_map* map =
1561379cacaSTyler Dauwalder 						reinterpret_cast<sparable_partition_map*>(header);
157d4e8b936STyler Dauwalder 					sparableCount++;
158d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
159d4e8b936STyler Dauwalder 				} else if (typeId.matches(kMetadataPartitionMapId)) {
160d4e8b936STyler Dauwalder 					PRINT(("map type: metadata\n"));
1611379cacaSTyler Dauwalder 					metadata_partition_map* map =
1621379cacaSTyler Dauwalder 						reinterpret_cast<metadata_partition_map*>(header);
163d4e8b936STyler Dauwalder 					metadataCount++;
164d4e8b936STyler Dauwalder 					(void)map;	// kill the warning for now
165d4e8b936STyler Dauwalder 				} else {
166d4e8b936STyler Dauwalder 					PRINT(("map type: unrecognized (`%.23s')\n",
167d4e8b936STyler Dauwalder 					       typeId.identifier()));
168d4e8b936STyler Dauwalder 					error = B_ERROR;
16921c162a3STyler Dauwalder 				}
170d4e8b936STyler Dauwalder 			} else {
171d4e8b936STyler Dauwalder 				PRINT(("Invalid partition type %d found!\n", header->type()));
172d4e8b936STyler Dauwalder 				error = B_ERROR;
173d4e8b936STyler Dauwalder 			}
17421c162a3STyler Dauwalder 			offset += header->length();
17521c162a3STyler Dauwalder 		}
17621c162a3STyler Dauwalder 	}
17721c162a3STyler Dauwalder 
178d4e8b936STyler Dauwalder 	// Do some checking as to what sorts of partitions we've actually found.
17921c162a3STyler Dauwalder 	if (!error) {
180d4e8b936STyler Dauwalder 		error = (physicalCount == 1 && virtualCount == 0
181d4e8b936STyler Dauwalder 		         && sparableCount == 0 && metadataCount == 0)
182d4e8b936STyler Dauwalder 		        || (physicalCount == 2 && virtualCount == 0
183d4e8b936STyler Dauwalder 		           && sparableCount == 0 && metadataCount == 0)
184d4e8b936STyler Dauwalder 		        ? B_OK : B_ERROR;
185d4e8b936STyler Dauwalder 		if (error) {
186d4e8b936STyler Dauwalder 			PRINT(("Invalid partition layout found:\n"));
187d4e8b936STyler Dauwalder 			PRINT(("  physical partitions: %d\n", physicalCount));
188d4e8b936STyler Dauwalder 			PRINT(("  virtual partitions:  %d\n", virtualCount));
189d4e8b936STyler Dauwalder 			PRINT(("  sparable partitions: %d\n", sparableCount));
190d4e8b936STyler Dauwalder 			PRINT(("  metadata partitions: %d\n", metadataCount));
191d4e8b936STyler Dauwalder 		}
192d4e8b936STyler Dauwalder 	}
193d4e8b936STyler Dauwalder 
194d4e8b936STyler Dauwalder 	// We're now going to start creating Icb's, which will expect
195d4e8b936STyler Dauwalder 	// certain parts of the volume to be initialized properly. Thus,
196d4e8b936STyler Dauwalder 	// we initialize those parts here.
197d4e8b936STyler Dauwalder 	if (!error) {
198d4e8b936STyler Dauwalder 		fDevice = device;
199d4e8b936STyler Dauwalder 		fOffset = offset;
200d4e8b936STyler Dauwalder 		fLength = length;
201d4e8b936STyler Dauwalder 		fBlockSize = blockSize;
202d4e8b936STyler Dauwalder 		fBlockShift = blockShift;
203d4e8b936STyler Dauwalder 	}
204d4e8b936STyler Dauwalder 
205d4e8b936STyler Dauwalder 	// At this point we've found a valid set of volume descriptors and
206d4e8b936STyler Dauwalder 	// our partitions are all set up. We now need to investigate the file
207d4e8b936STyler Dauwalder 	// set descriptor pointed to by the logical volume descriptor.
208d4e8b936STyler Dauwalder 	if (!error) {
209d4e8b936STyler Dauwalder 		MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
21021c162a3STyler Dauwalder 
211d8b4553aSTyler Dauwalder 		error = chunk.InitCheck();
21221c162a3STyler Dauwalder 
21321c162a3STyler Dauwalder 		if (!error) {
214d4e8b936STyler Dauwalder 			off_t address;
215d4e8b936STyler Dauwalder 			// Read in the file set descriptor
216d4e8b936STyler Dauwalder 			error = MapBlock(logicalVolumeDescriptor.file_set_address(),
217d4e8b936STyler Dauwalder 			 	             &address);
218d4e8b936STyler Dauwalder 			if (!error)
219d4e8b936STyler Dauwalder 				address <<= blockShift;
220d4e8b936STyler Dauwalder 			if (!error) {
221d4e8b936STyler Dauwalder 				ssize_t bytesRead = read_pos(device, address, chunk.Data(),
222d4e8b936STyler Dauwalder 				                             blockSize);
223d8b4553aSTyler Dauwalder 				if (bytesRead != ssize_t(blockSize)) {
224d4e8b936STyler Dauwalder 					error = B_IO_ERROR;
225d4e8b936STyler Dauwalder 					PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n",
226d4e8b936STyler Dauwalder 					       address, blockSize, bytesRead));
227d4e8b936STyler Dauwalder 				}
228d4e8b936STyler Dauwalder 			}
229d4e8b936STyler Dauwalder 			// See if it's valid, and if so, create the root icb
23021c162a3STyler Dauwalder 			if (!error) {
2311379cacaSTyler Dauwalder 				file_set_descriptor *fileSet =
2321379cacaSTyler Dauwalder 				 	reinterpret_cast<file_set_descriptor*>(chunk.Data());
23317b66e62STyler Dauwalder 				PDUMP(fileSet);
234d8b4553aSTyler Dauwalder 				error = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
235d8b4553aSTyler Dauwalder 				        ? B_OK : B_ERROR;
236d8b4553aSTyler Dauwalder 				if (!error)
23717b66e62STyler Dauwalder 					error = fileSet->tag().init_check(
23817b66e62STyler Dauwalder 					        logicalVolumeDescriptor.file_set_address().block());
239d8b4553aSTyler Dauwalder 				if (!error) {
24021c162a3STyler Dauwalder 					PDUMP(fileSet);
24121c162a3STyler Dauwalder 					fRootIcb = new Icb(this, fileSet->root_directory_icb());
24221c162a3STyler Dauwalder 					error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY;
243d8b4553aSTyler Dauwalder 				}
24421c162a3STyler Dauwalder 				if (!error) {
24521c162a3STyler Dauwalder 					error = new_vnode(Id(), RootIcb()->Id(), (void*)RootIcb());
24621c162a3STyler Dauwalder 					if (error) {
247d4e8b936STyler Dauwalder 						PRINT(("Error creating vnode for root icb! "
248d4e8b936STyler Dauwalder 						       "error = 0x%lx, `%s'\n", error,
249d4e8b936STyler Dauwalder 						       strerror(error)));
250d4e8b936STyler Dauwalder 						// Clean up the icb we created, since _Unset()
251d4e8b936STyler Dauwalder 						// won't do this for us.
252d4e8b936STyler Dauwalder 						delete fRootIcb;
253d4e8b936STyler Dauwalder 						fRootIcb = NULL;
254d4e8b936STyler Dauwalder 					}
255d4e8b936STyler Dauwalder 				}
256d4e8b936STyler Dauwalder 			}
25721c162a3STyler Dauwalder 		}
25821c162a3STyler Dauwalder 	}
25921c162a3STyler Dauwalder 
260d4e8b936STyler Dauwalder 	// If we've made it this far, we're good to go; set the volume
261d4e8b936STyler Dauwalder 	// name and then flag that we're mounted. On the other hand, if
262d4e8b936STyler Dauwalder 	// an error occurred, we need to clean things up.
26321c162a3STyler Dauwalder 	if (!error) {
264d4e8b936STyler Dauwalder 		fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
265d4e8b936STyler Dauwalder 		fMounted = true;
266d4e8b936STyler Dauwalder 	} else {
267d4e8b936STyler Dauwalder 		_Unset();
26821c162a3STyler Dauwalder 	}
26921c162a3STyler Dauwalder 
27021c162a3STyler Dauwalder 	RETURN(error);
2714ac73018STyler Dauwalder }
2724ac73018STyler Dauwalder 
273a71fd512STyler Dauwalder const char*
274a71fd512STyler Dauwalder Volume::Name() const {
275dce2dc5cSTyler Dauwalder 	return fName.Utf8();
276a71fd512STyler Dauwalder }
2773f4628f1STyler Dauwalder 
278d4e8b936STyler Dauwalder /*! \brief Maps the given logical block to a physical block.
2793f4628f1STyler Dauwalder */
2803f4628f1STyler Dauwalder status_t
2811379cacaSTyler Dauwalder Volume::MapBlock(long_address address, off_t *mappedBlock)
2820d383564STyler Dauwalder {
28321ea9aeaSTyler Dauwalder 	DEBUG_INIT_ETC("Volume",
2841379cacaSTyler Dauwalder 		           ("partition: %d, block: %ld, mappedBlock: %p",
2851379cacaSTyler Dauwalder 		           address.partition(), address.block(), mappedBlock));
286d4e8b936STyler Dauwalder 	status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
287d4e8b936STyler Dauwalder 	if (!error) {
288d4e8b936STyler Dauwalder 		Partition *partition = _GetPartition(address.partition());
289d4e8b936STyler Dauwalder 		error = partition ? B_OK : B_BAD_ADDRESS;
290d4e8b936STyler Dauwalder 		if (!error)
291d4e8b936STyler Dauwalder 			error = partition->MapBlock(address.block(), *mappedBlock);
2923f4628f1STyler Dauwalder 	}
293d4e8b936STyler Dauwalder 	RETURN(error);
2943f4628f1STyler Dauwalder }
2953f4628f1STyler Dauwalder 
296d4e8b936STyler Dauwalder /*! \brief Unsets the volume and deletes any partitions.
297d4e8b936STyler Dauwalder 
298d4e8b936STyler Dauwalder 	Does *not* delete the root icb object.
299d4e8b936STyler Dauwalder */
300d4e8b936STyler Dauwalder void
301d4e8b936STyler Dauwalder Volume::_Unset()
302d4e8b936STyler Dauwalder {
303d8b4553aSTyler Dauwalder 	DEBUG_INIT("Volume");
304d4e8b936STyler Dauwalder 	fId = 0;
305237ced11STyler Dauwalder 	if (fDevice >= 0)
306237ced11STyler Dauwalder 		close(fDevice);
307237ced11STyler Dauwalder 	fDevice = -1;
308d4e8b936STyler Dauwalder 	fMounted = false;
309d4e8b936STyler Dauwalder 	fOffset = 0;
310d4e8b936STyler Dauwalder 	fLength = 0;
311d4e8b936STyler Dauwalder 	fBlockSize = 0;
312d4e8b936STyler Dauwalder 	fBlockShift = 0;
313dce2dc5cSTyler Dauwalder 	fName.SetTo("", 0);
314d4e8b936STyler Dauwalder 	// delete our partitions
315d4e8b936STyler Dauwalder 	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
316d4e8b936STyler Dauwalder 		_SetPartition(i, NULL);
317d4e8b936STyler Dauwalder }
318d4e8b936STyler Dauwalder 
319d4e8b936STyler Dauwalder /*! \brief Sets the partition associated with the given number after
320d4e8b936STyler Dauwalder 	deleting any previously associated partition.
321d4e8b936STyler Dauwalder 
322d4e8b936STyler Dauwalder 	\param number The partition number (should be the same as the index
323d4e8b936STyler Dauwalder 	              into the lvd's partition map array).
324d4e8b936STyler Dauwalder 	\param partition The new partition (may be NULL).
3253f4628f1STyler Dauwalder */
3263f4628f1STyler Dauwalder status_t
327d4e8b936STyler Dauwalder Volume::_SetPartition(uint number, Partition *partition)
3283f4628f1STyler Dauwalder {
329d4e8b936STyler Dauwalder 	status_t error = number < UDF_MAX_PARTITION_MAPS
330d4e8b936STyler Dauwalder 	                 ? B_OK : B_BAD_VALUE;
331d4e8b936STyler Dauwalder 	if (!error) {
332d4e8b936STyler Dauwalder 		delete fPartitions[number];
333d4e8b936STyler Dauwalder 		fPartitions[number] = partition;
3343f4628f1STyler Dauwalder 	}
335d4e8b936STyler Dauwalder 	return error;
3360d383564STyler Dauwalder }
3370d383564STyler Dauwalder 
338d4e8b936STyler Dauwalder /*! \brief Returns the partition associated with the given number, or
339d4e8b936STyler Dauwalder 	NULL if no such partition exists or the number is invalid.
3403f4628f1STyler Dauwalder */
341d4e8b936STyler Dauwalder Partition*
342d4e8b936STyler Dauwalder Volume::_GetPartition(uint number)
3433f4628f1STyler Dauwalder {
344d4e8b936STyler Dauwalder 	return (number < UDF_MAX_PARTITION_MAPS)
345d4e8b936STyler Dauwalder 	       ? fPartitions[number] : NULL;
3463f4628f1STyler Dauwalder }
3473f4628f1STyler Dauwalder 
348