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