xref: /haiku/src/add-ons/kernel/file_systems/udf/Volume.cpp (revision e1f5f06be818ab1b1abf83a911131eddff7d664f)
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 Volume::~Volume()
33 {
34 	_Unset();
35 }
36 
37 /*! \brief Attempts to mount the given device.
38 
39 	\param volumeStart The block on the given device whereat the volume begins.
40 	\param volumeLength The block numBlocks of the volume on the given device.
41 */
42 status_t
43 Volume::Mount(const char *deviceName, off_t offset, off_t numBlocks,
44 	uint32 blockSize, uint32 flags)
45 {
46 	DEBUG_INIT_ETC("Volume",
47 	               ("deviceName: `%s', offset: %Ld, numBlocks: %Ld, blockSize: %ld, "
48                    "flags: %ld", deviceName, offset, numBlocks, blockSize, flags));
49 	if (!deviceName)
50 		RETURN(B_BAD_VALUE);
51 	if (Mounted()) {
52 		// Already mounted, thank you for asking
53 		RETURN(B_BUSY);
54 	}
55 
56 	// Open the device read only
57 	int device = open(deviceName, O_RDONLY);
58 	if (device < B_OK)
59 		RETURN(device);
60 
61 	status_t error = B_OK;
62 
63 	// If the device is actually a normal file, try to disable the cache
64 	// for the file in the parent filesystem
65 #if 0 //  _KERNEL_MODE
66 	struct stat stat;
67 	error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
68 	if (!error) {
69 		if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
70 			DIE(("Unable to disable cache of underlying file system.\n"));
71 		}
72 	}
73 #endif
74 
75 	logical_volume_descriptor logicalVolumeDescriptor;
76 	partition_descriptor partitionDescriptors[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, numBlocks, blockSize, blockShift,
83 	//			logicalVolumeDescriptor, partitionDescriptors,
84 	//			partitionDescriptorCount);
85 
86 	// Set up the block cache
87 	if (!error)
88 		fBlockCache = block_cache_create(device, numBlocks, blockSize, IsReadOnly());
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(nothrow) 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 						       "numBlocks: %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 = numBlocks;
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 		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 				PDUMP(fileSet);
232 				error = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
233 				        ? B_OK : B_ERROR;
234 				if (!error)
235 					error = fileSet->tag().init_check(
236 					        logicalVolumeDescriptor.file_set_address().block());
237 				if (!error) {
238 					PDUMP(fileSet);
239 					fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb());
240 					error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY;
241 				}
242 				if (!error) {
243 					//error = new_vnode(fFSVolume->Id(), RootIcb()->Id(), (void*)RootIcb());
244 					if (error) {
245 						PRINT(("Error creating vnode for root icb! "
246 						       "error = 0x%lx, `%s'\n", error,
247 						       strerror(error)));
248 						// Clean up the icb we created, since _Unset()
249 						// won't do this for us.
250 						delete fRootIcb;
251 						fRootIcb = NULL;
252 					}
253 				}
254 			}
255 		}
256 	}
257 
258 	// If we've made it this far, we're good to go; set the volume
259 	// name and then flag that we're mounted. On the other hand, if
260 	// an error occurred, we need to clean things up.
261 	if (!error) {
262 		fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
263 		fMounted = true;
264 	} else {
265 		_Unset();
266 	}
267 
268 	RETURN(error);
269 }
270 
271 const char*
272 Volume::Name() const {
273 	return fName.Utf8();
274 }
275 
276 /*! \brief Maps the given logical block to a physical block.
277 */
278 status_t
279 Volume::MapBlock(long_address address, off_t *mappedBlock)
280 {
281 	DEBUG_INIT_ETC("Volume",
282 		           ("partition: %d, block: %ld, mappedBlock: %p",
283 		           address.partition(), address.block(), mappedBlock));
284 	status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
285 	if (!error) {
286 		Partition *partition = _GetPartition(address.partition());
287 		error = partition ? B_OK : B_BAD_ADDRESS;
288 		if (!error)
289 			error = partition->MapBlock(address.block(), *mappedBlock);
290 	}
291 	RETURN(error);
292 }
293 
294 /*! \brief Unsets the volume and deletes any partitions.
295 
296 	Does *not* delete the root icb object.
297 */
298 void
299 Volume::_Unset()
300 {
301 	DEBUG_INIT("Volume");
302 	fFSVolume->id = 0;
303 	if (fDevice >= 0) {
304 		block_cache_delete(fBlockCache, true);
305 		close(fDevice);
306 	}
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