xref: /haiku/src/add-ons/kernel/file_systems/udf/Volume.cpp (revision ed3ea3b05cf99c2f9e615a78bfcc526e8d468615)
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. */
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 
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
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 = %Ld, length = %Ld, "
52 		"blockSize: %ld, flags: %ld\n", deviceName, offset, length, blockSize,
53 		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: %ld, length: %ld)\n", map->partition_number(),
138 						descriptor->start(), descriptor->length()));
139 					status = _SetPartition(i, partition);
140 					if (!status)
141 						physicalCount++;
142 				}
143 			} else {
144 				TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
145 				status = B_ERROR;
146 			}
147 		} else if (header->type() == 2) {
148 			// Figure out what kind of type 2 partition map we have based
149 			// on the type identifier
150 			const entity_id &typeId = header->partition_type_id();
151 			DUMP(typeId);
152 			DUMP(kSparablePartitionMapId);
153 			if (typeId.matches(kVirtualPartitionMapId)) {
154 				TRACE(("map type: virtual\n"));
155 				virtual_partition_map* map =
156 					reinterpret_cast<virtual_partition_map*>(header);
157 				virtualCount++;
158 				(void)map;	// kill the warning for now
159 			} else if (typeId.matches(kSparablePartitionMapId)) {
160 				TRACE(("map type: sparable\n"));
161 				sparable_partition_map* map =
162 					reinterpret_cast<sparable_partition_map*>(header);
163 				sparableCount++;
164 				(void)map;	// kill the warning for now
165 			} else if (typeId.matches(kMetadataPartitionMapId)) {
166 				TRACE(("map type: metadata\n"));
167 				metadata_partition_map* map =
168 					reinterpret_cast<metadata_partition_map*>(header);
169 
170 
171 				// Find the corresponding partition descriptor
172 				partition_descriptor *descriptor = NULL;
173 				for (uint8 j = 0; j < partitionDescriptorCount; j++) {
174 					if (map->partition_number() ==
175 						partitionDescriptors[j].partition_number()) {
176 						descriptor = &partitionDescriptors[j];
177 						break;
178 					}
179 				}
180 				Partition *parent = _GetPartition(map->partition_number());
181 				// Create and add the partition
182 				if (descriptor != NULL && parent != NULL) {
183 					MetadataPartition *partition
184 						= new(nothrow) MetadataPartition(this,
185 							map->partition_number(), *parent,
186 							map->metadata_file_location(),
187 							map->metadata_mirror_file_location(),
188 							map->metadata_bitmap_file_location(),
189 							map->allocation_unit_size(),
190 							map->alignment_unit_size(),
191 							map->flags() & 1);
192 					status = partition ? partition->InitCheck() : B_NO_MEMORY;
193 					if (!status) {
194 						TRACE(("Volume::Mount: adding MetadataPartition()"));
195 						status = _SetPartition(i, partition);
196 						if (status == B_OK)
197 							metadataCount++;
198 					} else {
199 						TRACE_ERROR(("Volume::Mount: metadata partition "
200 							"creation failed! 0x%lx\n", status));
201 					}
202 				} else {
203 					TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
204 					status = B_ERROR;
205 				}
206 			} else {
207 				TRACE(("map type: unrecognized (`%.23s')\n",
208 				       typeId.identifier()));
209 				status = B_ERROR;
210 			}
211 		} else {
212 			TRACE(("Invalid partition type %d found!\n", header->type()));
213 			status = B_ERROR;
214 		}
215 		offset += header->length();
216 	}
217 
218 	// Do some checking as to what sorts of partitions we've actually found.
219 	if (!status) {
220 		status = (physicalCount == 1 && virtualCount == 0
221 		         && sparableCount == 0)
222 		        || (physicalCount == 2 && virtualCount == 0
223 		           && sparableCount == 0)
224 		        ? B_OK : B_ERROR;
225 		if (status) {
226 			TRACE(("Invalid partition layout found:\n"));
227 			TRACE(("  physical partitions: %d\n", physicalCount));
228 			TRACE(("  virtual partitions:  %d\n", virtualCount));
229 			TRACE(("  sparable partitions: %d\n", sparableCount));
230 			TRACE(("  metadata partitions: %d\n", metadataCount));
231 		}
232 	}
233 
234 	// We're now going to start creating Icb's, which will expect
235 	// certain parts of the volume to be initialized properly. Thus,
236 	// we initialize those parts here.
237 	if (!status) {
238 		fDevice = device;
239 		fOffset = offset;
240 		fLength = length;
241 		fBlockSize = blockSize;
242 		fBlockShift = blockShift;
243 	}
244 	TRACE(("Volume::Mount: device = %d, offset = %Ld, length = %Ld, "
245 		"blockSize = %ld, blockShift = %ld\n", device, offset, length,
246 		blockSize, blockShift));
247 	// At this point we've found a valid set of volume descriptors and
248 	// our partitions are all set up. We now need to investigate the file
249 	// set descriptor pointed to by the logical volume descriptor.
250 	if (!status) {
251 		TRACE(("Volume::Mount: Partition has been set up\n"));
252 		MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
253 
254 		status = chunk.InitCheck();
255 
256 		if (!status) {
257 			off_t address;
258 			// Read in the file set descriptor
259 			status = MapBlock(logicalVolumeDescriptor.file_set_address(),
260 				&address);
261 			if (!status)
262 				address <<= blockShift;
263 			if (!status) {
264 				ssize_t bytesRead
265 					= read_pos(device, address, chunk.Data(), blockSize);
266 				if (bytesRead != ssize_t(blockSize)) {
267 					status = B_IO_ERROR;
268 					TRACE_ERROR(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n",
269 						address, blockSize, bytesRead));
270 				}
271 			}
272 			// See if it's valid, and if so, create the root icb
273 			if (!status) {
274 				file_set_descriptor *fileSet =
275 				 	reinterpret_cast<file_set_descriptor*>(chunk.Data());
276 				PDUMP(fileSet);
277 				status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
278 				        ? B_OK : B_ERROR;
279 				if (!status)
280 					status = fileSet->tag().init_check(
281 					        logicalVolumeDescriptor.file_set_address().block());
282 				if (!status) {
283 					PDUMP(fileSet);
284 					fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb());
285 					if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK)
286 						return B_NO_MEMORY;
287 				}
288 
289 				TRACE(("Volume::Mount: Root Node id = %Ld\n", fRootIcb->Id()));
290 				if (!status) {
291 					status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb,
292 						&gUDFVnodeOps, fRootIcb->Mode(), 0);
293 					if (status != B_OK) {
294 						TRACE_ERROR(("Error creating vnode for root icb! "
295 						       "status = 0x%lx, `%s'\n", status,
296 						       strerror(status)));
297 						// Clean up the icb we created, since _Unset()
298 						// won't do this for us.
299 						delete fRootIcb;
300 						fRootIcb = NULL;
301 					}
302 					TRACE(("Volume::Mount: Root vnode published. Id = %Ld\n",
303 						fRootIcb->Id()));
304 				}
305 			}
306 		}
307 	}
308 
309 	// If we've made it this far, we're good to go; set the volume
310 	// name and then flag that we're mounted. On the other hand, if
311 	// an error occurred, we need to clean things up.
312 	if (!status) {
313 		fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
314 		fMounted = true;
315 	} else {
316 		_Unset();
317 	}
318 
319 	RETURN(status);
320 }
321 
322 
323 const char*
324 Volume::Name() const {
325 	return fName.Utf8();
326 }
327 
328 /*! \brief Maps the given logical block to a physical block.
329 */
330 status_t
331 Volume::MapBlock(long_address address, off_t *mappedBlock)
332 {
333 	TRACE(("Volume::MapBlock: partition = %d, block = %ld, mappedBlock = %p\n",
334 		address.partition(), address.block(), mappedBlock));
335 	DEBUG_INIT_ETC("Volume", ("partition = %d, block = %ld, mappedBlock = %p",
336 		address.partition(), address.block(), mappedBlock));
337 	status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
338 	if (!error) {
339 		Partition *partition = _GetPartition(address.partition());
340 		error = partition ? B_OK : B_BAD_ADDRESS;
341 		if (!error)
342 			error = partition->MapBlock(address.block(), *mappedBlock);
343 	}
344 	RETURN(error);
345 }
346 
347 /*! \brief Unsets the volume and deletes any partitions.
348 
349 	Does *not* delete the root icb object.
350 */
351 void
352 Volume::_Unset()
353 {
354 	DEBUG_INIT("Volume");
355 	// delete our partitions
356 	for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
357 		_SetPartition(i, NULL);
358 	fFSVolume->id = 0;
359 	if (fDevice >= 0) {
360 		block_cache_delete(fBlockCache, true);
361 		close(fDevice);
362 	}
363 	fBlockCache = NULL;
364 	fDevice = -1;
365 	fMounted = false;
366 	fOffset = 0;
367 	fLength = 0;
368 	fBlockSize = 0;
369 	fBlockShift = 0;
370 	fName.SetTo("", 0);
371 }
372 
373 /*! \brief Sets the partition associated with the given number after
374 	deleting any previously associated partition.
375 
376 	\param number The partition number (should be the same as the index
377 	              into the lvd's partition map array).
378 	\param partition The new partition (may be NULL).
379 */
380 status_t
381 Volume::_SetPartition(uint number, Partition *partition)
382 {
383 	status_t error = number < UDF_MAX_PARTITION_MAPS
384 	                 ? B_OK : B_BAD_VALUE;
385 	if (!error) {
386 		delete fPartitions[number];
387 		fPartitions[number] = partition;
388 	}
389 	return error;
390 }
391 
392 /*! \brief Returns the partition associated with the given number, or
393 	NULL if no such partition exists or the number is invalid.
394 */
395 Partition*
396 Volume::_GetPartition(uint number)
397 {
398 	return (number < UDF_MAX_PARTITION_MAPS)
399 	       ? fPartitions[number] : NULL;
400 }
401