1/*
2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com
4 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
5 * Distributed under the terms of the MIT License.
6 */
7
8#include "Volume.h"
9
10#include "Icb.h"
11#include "MemoryChunk.h"
12#include "MetadataPartition.h"
13#include "PhysicalPartition.h"
14#include "Recognition.h"
15
16extern fs_volume_ops gUDFVolumeOps;
17extern fs_vnode_ops gUDFVnodeOps;
18
19/*! \brief Creates an unmounted volume with the given id. */
20Volume::Volume(fs_volume *fsVolume)
21	:
22	fBlockCache(NULL),
23	fBlockShift(0),
24	fBlockSize(0),
25	fDevice(-1),
26	fFSVolume(fsVolume),
27	fLength(0),
28	fMounted(false),
29	fOffset(0),
30	fRootIcb(NULL)
31{
32	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
33		fPartitions[i] = NULL;
34}
35
36
37Volume::~Volume()
38{
39	_Unset();
40}
41
42
43/*! \brief Attempts to mount the given device.
44
45	\param lenght The length of the device in number of blocks
46*/
47status_t
48Volume::Mount(const char *deviceName, off_t offset, off_t length,
49	uint32 blockSize, uint32 flags)
50{
51	TRACE(("Volume::Mount: deviceName = `%s', offset = %" B_PRIdOFF ", length "
52		"= %" B_PRIdOFF ", blockSize: %" B_PRIu32 ", flags: %" B_PRIu32 "\n",
53		deviceName, offset, length, blockSize, flags));
54	if (!deviceName)
55		return B_BAD_VALUE;
56	if (Mounted())
57		// Already mounted, thank you for asking
58		return B_BUSY;
59
60	// Open the device read only
61	int device = open(deviceName, O_RDONLY);
62	if (device < B_OK) {
63		TRACE_ERROR(("Volume::Mount: failed to open device = %s\n", deviceName));
64		return device;
65	}
66
67	DEBUG_INIT_ETC("Volume", ("deviceName: %s", deviceName));
68	status_t status = B_OK;
69
70	// If the device is actually a normal file, try to disable the cache
71	// for the file in the parent filesystem
72#if 0 //  _KERNEL_MODE
73	struct stat stat;
74	error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
75	if (!error) {
76		if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
77			DIE(("Unable to disable cache of underlying file system.\n"));
78		}
79	}
80#endif
81
82	logical_volume_descriptor logicalVolumeDescriptor;
83	partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
84	uint8 partitionDescriptorCount;
85	uint32 blockShift;
86
87	// Run through the volume recognition and descriptor sequences to
88	// see if we have a potentially valid UDF volume on our hands
89	status = udf_recognize(device, offset, length, blockSize, blockShift,
90				fPrimaryVolumeDescriptor, logicalVolumeDescriptor,
91				partitionDescriptors, partitionDescriptorCount);
92
93	// Set up the block cache
94	if (!status) {
95		TRACE(("Volume::Mount: partition recognized\n"));
96		fBlockCache = block_cache_create(device, length, blockSize, IsReadOnly());
97	} else {
98		TRACE_ERROR(("Volume::Mount: failed to recognize partition\n"));
99		return status;
100	}
101
102	int physicalCount = 0;
103	int virtualCount = 0;
104	int sparableCount = 0;
105	int metadataCount = 0;
106
107	// Set up the partitions
108	// Set up physical and sparable partitions first
109	offset = 0;
110	for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count()
111	     && !status; i++)
112	{
113		uint8 *maps = logicalVolumeDescriptor.partition_maps();
114		partition_map_header *header = (partition_map_header *)(maps + offset);
115		TRACE(("Volume::Mount: partition map %d (type %d):\n", i,
116			header->type()));
117		if (header->type() == 1) {
118			TRACE(("Volume::Mount: map type -> physical\n"));
119			physical_partition_map* map = (physical_partition_map *)header;
120			// Find the corresponding partition descriptor
121			partition_descriptor *descriptor = NULL;
122			for (uint8 j = 0; j < partitionDescriptorCount; j++) {
123				if (map->partition_number() ==
124				    partitionDescriptors[j].partition_number()) {
125					descriptor = &partitionDescriptors[j];
126					break;
127				}
128			}
129			// Create and add the partition
130			if (descriptor) {
131				PhysicalPartition *partition
132					= new(nothrow) PhysicalPartition(map->partition_number(),
133						descriptor->start(), descriptor->length());
134				status = partition ? B_OK : B_NO_MEMORY;
135				if (!status) {
136					TRACE(("Volume::Mount: adding PhysicalPartition(number: %d, "
137						"start: %" B_PRIu32 ", length: %" B_PRIu32 ")\n",
138						map->partition_number(), descriptor->start(),
139						descriptor->length()));
140					status = _SetPartition(i, partition);
141					if (!status)
142						physicalCount++;
143				}
144			} else {
145				TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
146				status = B_ERROR;
147			}
148		} else if (header->type() == 2) {
149			// Figure out what kind of type 2 partition map we have based
150			// on the type identifier
151			const entity_id &typeId = header->partition_type_id();
152			DUMP(typeId);
153			DUMP(kSparablePartitionMapId);
154			if (typeId.matches(kVirtualPartitionMapId)) {
155				TRACE(("map type: virtual\n"));
156				virtual_partition_map* map =
157					reinterpret_cast<virtual_partition_map*>(header);
158				virtualCount++;
159				(void)map;	// kill the warning for now
160			} else if (typeId.matches(kSparablePartitionMapId)) {
161				TRACE(("map type: sparable\n"));
162				sparable_partition_map* map =
163					reinterpret_cast<sparable_partition_map*>(header);
164				sparableCount++;
165				(void)map;	// kill the warning for now
166			} else if (typeId.matches(kMetadataPartitionMapId)) {
167				TRACE(("map type: metadata\n"));
168				metadata_partition_map* map =
169					reinterpret_cast<metadata_partition_map*>(header);
170
171
172				// Find the corresponding partition descriptor
173				partition_descriptor *descriptor = NULL;
174				for (uint8 j = 0; j < partitionDescriptorCount; j++) {
175					if (map->partition_number() ==
176						partitionDescriptors[j].partition_number()) {
177						descriptor = &partitionDescriptors[j];
178						break;
179					}
180				}
181				Partition *parent = _GetPartition(map->partition_number());
182				// Create and add the partition
183				if (descriptor != NULL && parent != NULL) {
184					MetadataPartition *partition
185						= new(nothrow) MetadataPartition(this,
186							map->partition_number(), *parent,
187							map->metadata_file_location(),
188							map->metadata_mirror_file_location(),
189							map->metadata_bitmap_file_location(),
190							map->allocation_unit_size(),
191							map->alignment_unit_size(),
192							map->flags() & 1);
193					status = partition ? partition->InitCheck() : B_NO_MEMORY;
194					if (!status) {
195						TRACE(("Volume::Mount: adding MetadataPartition()"));
196						status = _SetPartition(i, partition);
197						if (status == B_OK)
198							metadataCount++;
199					} else {
200						TRACE_ERROR(("Volume::Mount: metadata partition "
201							"creation failed! 0x%" B_PRIx32 "\n", status));
202					}
203				} else {
204					TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
205					status = B_ERROR;
206				}
207			} else {
208				TRACE(("map type: unrecognized (`%.23s')\n",
209				       typeId.identifier()));
210				status = B_ERROR;
211			}
212		} else {
213			TRACE(("Invalid partition type %d found!\n", header->type()));
214			status = B_ERROR;
215		}
216		offset += header->length();
217	}
218
219	// Do some checking as to what sorts of partitions we've actually found.
220	if (!status) {
221		status = (physicalCount == 1 && virtualCount == 0
222		         && sparableCount == 0)
223		        || (physicalCount == 2 && virtualCount == 0
224		           && sparableCount == 0)
225		        ? B_OK : B_ERROR;
226		if (status) {
227			TRACE(("Invalid partition layout found:\n"));
228			TRACE(("  physical partitions: %d\n", physicalCount));
229			TRACE(("  virtual partitions:  %d\n", virtualCount));
230			TRACE(("  sparable partitions: %d\n", sparableCount));
231			TRACE(("  metadata partitions: %d\n", metadataCount));
232		}
233	}
234
235	// We're now going to start creating Icb's, which will expect
236	// certain parts of the volume to be initialized properly. Thus,
237	// we initialize those parts here.
238	if (!status) {
239		fDevice = device;
240		fOffset = offset;
241		fLength = length;
242		fBlockSize = blockSize;
243		fBlockShift = blockShift;
244	}
245	TRACE(("Volume::Mount: device = %d, offset = %" B_PRIdOFF ", length = %"
246		B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n",
247		device, offset, length, blockSize, blockShift));
248	// At this point we've found a valid set of volume descriptors and
249	// our partitions are all set up. We now need to investigate the file
250	// set descriptor pointed to by the logical volume descriptor.
251	if (!status) {
252		TRACE(("Volume::Mount: Partition has been set up\n"));
253		MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
254
255		status = chunk.InitCheck();
256
257		if (!status) {
258			off_t address;
259			// Read in the file set descriptor
260			status = MapBlock(logicalVolumeDescriptor.file_set_address(),
261				&address);
262			if (!status)
263				address <<= blockShift;
264			if (!status) {
265				ssize_t bytesRead
266					= read_pos(device, address, chunk.Data(), blockSize);
267				if (bytesRead != ssize_t(blockSize)) {
268					status = B_IO_ERROR;
269					TRACE_ERROR(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32
270						") failed with: 0x%lx\n", address, blockSize,
271						bytesRead));
272				}
273			}
274			// See if it's valid, and if so, create the root icb
275			if (!status) {
276				file_set_descriptor *fileSet =
277				 	reinterpret_cast<file_set_descriptor*>(chunk.Data());
278				PDUMP(fileSet);
279				status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
280				        ? B_OK : B_ERROR;
281				if (!status)
282					status = fileSet->tag().init_check(
283					        logicalVolumeDescriptor.file_set_address().block());
284				if (!status) {
285					PDUMP(fileSet);
286					fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb());
287					if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK)
288						return B_NO_MEMORY;
289				}
290
291				TRACE(("Volume::Mount: Root Node id = %" B_PRIdINO "\n",
292					fRootIcb->Id()));
293				if (!status) {
294					status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb,
295						&gUDFVnodeOps, fRootIcb->Mode(), 0);
296					if (status != B_OK) {
297						TRACE_ERROR(("Error creating vnode for root icb! "
298						       "status = 0x%" B_PRIx32 ", `%s'\n", status,
299						       strerror(status)));
300						// Clean up the icb we created, since _Unset()
301						// won't do this for us.
302						delete fRootIcb;
303						fRootIcb = NULL;
304					}
305					TRACE(("Volume::Mount: Root vnode published. Id = %"
306						B_PRIdINO "\n", fRootIcb->Id()));
307				}
308			}
309		}
310	}
311
312	// If we've made it this far, we're good to go; set the volume
313	// name and then flag that we're mounted. On the other hand, if
314	// an error occurred, we need to clean things up.
315	if (!status) {
316		fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
317		fMounted = true;
318	} else {
319		_Unset();
320	}
321
322	RETURN(status);
323}
324
325
326const char*
327Volume::Name() const {
328	return fName.Utf8();
329}
330
331/*! \brief Maps the given logical block to a physical block.
332*/
333status_t
334Volume::MapBlock(long_address address, off_t *mappedBlock)
335{
336	TRACE(("Volume::MapBlock: partition = %d, block = %" B_PRIu32
337		", mappedBlock = %p\n", address.partition(), address.block(),
338		mappedBlock));
339	DEBUG_INIT_ETC("Volume", ("partition = %d, block = %" B_PRIu32
340		", mappedBlock = %p", address.partition(), address.block(),
341		mappedBlock));
342	status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
343	if (!error) {
344		Partition *partition = _GetPartition(address.partition());
345		error = partition ? B_OK : B_BAD_ADDRESS;
346		if (!error)
347			error = partition->MapBlock(address.block(), *mappedBlock);
348	}
349	RETURN(error);
350}
351
352/*! \brief Unsets the volume and deletes any partitions.
353
354	Does *not* delete the root icb object.
355*/
356void
357Volume::_Unset()
358{
359	DEBUG_INIT("Volume");
360	// delete our partitions
361	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
362		_SetPartition(i, NULL);
363	fFSVolume->id = 0;
364	if (fDevice >= 0) {
365		block_cache_delete(fBlockCache, true);
366		close(fDevice);
367	}
368	fBlockCache = NULL;
369	fDevice = -1;
370	fMounted = false;
371	fOffset = 0;
372	fLength = 0;
373	fBlockSize = 0;
374	fBlockShift = 0;
375	fName.SetTo("", 0);
376}
377
378/*! \brief Sets the partition associated with the given number after
379	deleting any previously associated partition.
380
381	\param number The partition number (should be the same as the index
382	              into the lvd's partition map array).
383	\param partition The new partition (may be NULL).
384*/
385status_t
386Volume::_SetPartition(uint number, Partition *partition)
387{
388	status_t error = number < UDF_MAX_PARTITION_MAPS
389	                 ? B_OK : B_BAD_VALUE;
390	if (!error) {
391		delete fPartitions[number];
392		fPartitions[number] = partition;
393	}
394	return error;
395}
396
397/*! \brief Returns the partition associated with the given number, or
398	NULL if no such partition exists or the number is invalid.
399*/
400Partition*
401Volume::_GetPartition(uint number)
402{
403	return (number < UDF_MAX_PARTITION_MAPS)
404	       ? fPartitions[number] : NULL;
405}
406