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