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