xref: /haiku/src/add-ons/kernel/file_systems/udf/Volume.cpp (revision b8ded2f89783a220c7b3019d48266a682cc79158)
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 
16 extern fs_volume_ops gUDFVolumeOps;
17 extern fs_vnode_ops gUDFVnodeOps;
18 
19 /*! \brief Creates an unmounted volume with the given id. */
Volume(fs_volume * fsVolume)20 Volume::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 
~Volume()37 Volume::~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 */
47 status_t
Mount(const char * deviceName,off_t offset,off_t length,uint32 blockSize,uint32 flags)48 Volume::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 
326 const char*
Name() const327 Volume::Name() const {
328 	return fName.Utf8();
329 }
330 
331 /*! \brief Maps the given logical block to a physical block.
332 */
333 status_t
MapBlock(long_address address,off_t * mappedBlock)334 Volume::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 */
356 void
_Unset()357 Volume::_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 */
385 status_t
_SetPartition(uint number,Partition * partition)386 Volume::_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 */
400 Partition*
_GetPartition(uint number)401 Volume::_GetPartition(uint number)
402 {
403 	return (number < UDF_MAX_PARTITION_MAPS)
404 	       ? fPartitions[number] : NULL;
405 }
406