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