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
16using namespace Udf;
17
18/*! \brief Creates an unmounted volume with the given id.
19*/
20Volume::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
34Volume::~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*/
44status_t
45Volume::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 _KERNEL_MODE
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(nothrow) 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(nothrow) 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
273const char*
274Volume::Name() const {
275	return fName.Utf8();
276}
277
278/*! \brief Maps the given logical block to a physical block.
279*/
280status_t
281Volume::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*/
300void
301Volume::_Unset()
302{
303	DEBUG_INIT("Volume");
304	fId = 0;
305	if (fDevice >= 0) {
306		remove_cached_device_blocks(fDevice, NO_WRITES);
307		close(fDevice);
308	}
309	fDevice = -1;
310	fMounted = false;
311	fOffset = 0;
312	fLength = 0;
313	fBlockSize = 0;
314	fBlockShift = 0;
315	fName.SetTo("", 0);
316	// delete our partitions
317	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
318		_SetPartition(i, NULL);
319}
320
321/*! \brief Sets the partition associated with the given number after
322	deleting any previously associated partition.
323
324	\param number The partition number (should be the same as the index
325	              into the lvd's partition map array).
326	\param partition The new partition (may be NULL).
327*/
328status_t
329Volume::_SetPartition(uint number, Partition *partition)
330{
331	status_t error = number < UDF_MAX_PARTITION_MAPS
332	                 ? B_OK : B_BAD_VALUE;
333	if (!error) {
334		delete fPartitions[number];
335		fPartitions[number] = partition;
336	}
337	return error;
338}
339
340/*! \brief Returns the partition associated with the given number, or
341	NULL if no such partition exists or the number is invalid.
342*/
343Udf::Partition*
344Volume::_GetPartition(uint number)
345{
346	return (number < UDF_MAX_PARTITION_MAPS)
347	       ? fPartitions[number] : NULL;
348}
349
350