xref: /haiku/src/system/boot/loader/partitions.cpp (revision cf02b29e4e0dd6d61c4bb25fcc8620e99d4908bf)
1 /*
2  * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "RootFileSystem.h"
8 
9 #include <boot/FileMapDisk.h>
10 #include <boot/partitions.h>
11 #include <boot/vfs.h>
12 #include <boot/platform.h>
13 #include <boot/stage2.h>
14 #include <boot/stdio.h>
15 #include <ddm_modules.h>
16 #include <util/kernel_cpp.h>
17 
18 #include <unistd.h>
19 #include <string.h>
20 
21 using namespace boot;
22 
23 #define TRACE_PARTITIONS
24 #ifdef TRACE_PARTITIONS
25 #	define TRACE(x) dprintf x
26 #else
27 #	define TRACE(x) ;
28 #endif
29 
30 
31 /* supported partition modules */
32 
33 static const partition_module_info *sPartitionModules[] = {
34 #ifdef BOOT_SUPPORT_PARTITION_AMIGA
35 	&gAmigaPartitionModule,
36 #endif
37 #ifdef BOOT_SUPPORT_PARTITION_EFI
38 	&gEFIPartitionModule,
39 #endif
40 #ifdef BOOT_SUPPORT_PARTITION_INTEL
41 	&gIntelPartitionMapModule,
42 	&gIntelExtendedPartitionModule,
43 #endif
44 #ifdef BOOT_SUPPORT_PARTITION_APPLE
45 	&gApplePartitionModule,
46 #endif
47 };
48 static const int32 sNumPartitionModules = sizeof(sPartitionModules)
49 	/ sizeof(partition_module_info *);
50 
51 /* supported file system modules */
52 
53 static file_system_module_info *sFileSystemModules[] = {
54 #ifdef BOOT_SUPPORT_FILE_SYSTEM_BFS
55 	&gBFSFileSystemModule,
56 #endif
57 #ifdef BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
58 	&gAmigaFFSFileSystemModule,
59 #endif
60 #ifdef BOOT_SUPPORT_FILE_SYSTEM_FAT
61 	&gFATFileSystemModule,
62 #endif
63 #ifdef BOOT_SUPPORT_FILE_SYSTEM_HFS_PLUS
64 	&gHFSPlusFileSystemModule,
65 #endif
66 #ifdef BOOT_SUPPORT_FILE_SYSTEM_TARFS
67 	&gTarFileSystemModule,
68 #endif
69 };
70 static const int32 sNumFileSystemModules = sizeof(sFileSystemModules)
71 	/ sizeof(file_system_module_info *);
72 
73 extern NodeList gPartitions;
74 
75 
76 namespace boot {
77 
78 /*! A convenience class to automatically close a
79 	file descriptor upon deconstruction.
80 */
81 class NodeOpener {
82 public:
83 	NodeOpener(Node *node, int mode)
84 	{
85 		fFD = open_node(node, mode);
86 	}
87 
88 	~NodeOpener()
89 	{
90 		close(fFD);
91 	}
92 
93 	int Descriptor() const { return fFD; }
94 
95 private:
96 	int		fFD;
97 };
98 
99 
100 //	#pragma mark -
101 
102 
103 Partition::Partition(int fd)
104 	:
105 	fParent(NULL),
106 	fIsFileSystem(false),
107 	fIsPartitioningSystem(false)
108 {
109 	TRACE(("%p Partition::Partition\n", this));
110 
111 	memset((partition_data *)this, 0, sizeof(partition_data));
112 	id = (partition_id)this;
113 
114 	// it's safe to close the file
115 	fFD = dup(fd);
116 }
117 
118 
119 Partition::~Partition()
120 {
121 	TRACE(("%p Partition::~Partition\n", this));
122 
123 	// Tell the children that their parent is gone
124 
125 	NodeIterator iterator = gPartitions.GetIterator();
126 	Partition *child;
127 
128 	while ((child = (Partition *)iterator.Next()) != NULL) {
129 		if (child->Parent() == this)
130 			child->SetParent(NULL);
131 	}
132 
133 	close(fFD);
134 }
135 
136 
137 void
138 Partition::SetParent(Partition *parent)
139 {
140 	TRACE(("%p Partition::SetParent %p\n", this, parent));
141 	fParent = parent;
142 }
143 
144 
145 Partition *
146 Partition::Parent() const
147 {
148 	//TRACE(("%p Partition::Parent is %p\n", this, fParent));
149 	return fParent;
150 }
151 
152 
153 ssize_t
154 Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
155 {
156 	if (position > this->size)
157 		return 0;
158 	if (position < 0)
159 		return B_BAD_VALUE;
160 
161 	if (position + bufferSize > this->size)
162 		bufferSize = this->size - position;
163 
164 	return read_pos(fFD, this->offset + position, buffer, bufferSize);
165 }
166 
167 
168 ssize_t
169 Partition::WriteAt(void *cookie, off_t position, const void *buffer,
170 	size_t bufferSize)
171 {
172 	if (position > this->size)
173 		return 0;
174 	if (position < 0)
175 		return B_BAD_VALUE;
176 
177 	if (position + bufferSize > this->size)
178 		bufferSize = this->size - position;
179 
180 	return write_pos(fFD, this->offset + position, buffer, bufferSize);
181 }
182 
183 
184 off_t
185 Partition::Size() const
186 {
187 	struct stat stat;
188 	if (fstat(fFD, &stat) == B_OK)
189 		return stat.st_size;
190 
191 	return Node::Size();
192 }
193 
194 
195 int32
196 Partition::Type() const
197 {
198 	struct stat stat;
199 	if (fstat(fFD, &stat) == B_OK)
200 		return stat.st_mode;
201 
202 	return Node::Type();
203 }
204 
205 
206 Partition *
207 Partition::AddChild()
208 {
209 	Partition *child = new Partition(fFD);
210 	TRACE(("%p Partition::AddChild %p\n", this, child));
211 	if (child == NULL)
212 		return NULL;
213 
214 	child->SetParent(this);
215 	child_count++;
216 	fChildren.Add(child);
217 
218 	return child;
219 }
220 
221 
222 status_t
223 Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
224 {
225 	TRACE(("%p Partition::_Mount check for file_system: %s\n",
226 		this, module->pretty_name));
227 
228 	Directory *fileSystem;
229 	if (module->get_file_system(this, &fileSystem) == B_OK) {
230 		gRoot->AddVolume(fileSystem, this);
231 		if (_fileSystem)
232 			*_fileSystem = fileSystem;
233 
234 		// remember the module name that mounted us
235 		fModuleName = module->module_name;
236 
237 		fIsFileSystem = true;
238 
239 #ifdef BOOT_SUPPORT_FILE_MAP_DISK
240 		static int fileMapDiskDepth = 0;
241 		// if we aren't already mounting an image
242 		if (!fileMapDiskDepth++) {
243 			// see if it contains an image file we could mount in turn
244 			FileMapDisk *disk = FileMapDisk::FindAnyFileMapDisk(fileSystem);
245 			if (disk) {
246 				TRACE(("%p Partition::_Mount: found FileMapDisk\n", this));
247 				disk->RegisterFileMapBootItem();
248 				add_partitions_for(disk, true, false);
249 			}
250 		}
251 		fileMapDiskDepth--;
252 #endif
253 
254 		return B_OK;
255 	}
256 
257 	return B_BAD_VALUE;
258 }
259 
260 
261 status_t
262 Partition::Mount(Directory **_fileSystem, bool isBootDevice)
263 {
264 	if (isBootDevice && gKernelArgs.boot_volume.GetBool(
265 			BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) {
266 		return _Mount(&gTarFileSystemModule, _fileSystem);
267 	}
268 
269 	for (int32 i = 0; i < sNumFileSystemModules; i++) {
270 		status_t status = _Mount(sFileSystemModules[i], _fileSystem);
271 		if (status == B_OK)
272 			return B_OK;
273 	}
274 
275 	return B_ENTRY_NOT_FOUND;
276 }
277 
278 
279 status_t
280 Partition::Scan(bool mountFileSystems, bool isBootDevice)
281 {
282 	// scan for partitions first (recursively all eventual children as well)
283 
284 	TRACE(("%p Partition::Scan()\n", this));
285 
286 	// if we were not booted from the real boot device, we won't scan
287 	// the device we were booted from (which is likely to be a slow
288 	// floppy or CD)
289 	if (isBootDevice && gKernelArgs.boot_volume.GetBool(
290 			BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) {
291 		return B_ENTRY_NOT_FOUND;
292 	}
293 
294 	const partition_module_info *bestModule = NULL;
295 	void *bestCookie = NULL;
296 	float bestPriority = -1;
297 
298 	for (int32 i = 0; i < sNumPartitionModules; i++) {
299 		const partition_module_info *module = sPartitionModules[i];
300 		void *cookie = NULL;
301 		NodeOpener opener(this, O_RDONLY);
302 
303 		TRACE(("check for partitioning_system: %s\n", module->pretty_name));
304 
305 		float priority
306 			= module->identify_partition(opener.Descriptor(), this, &cookie);
307 		if (priority < 0.0)
308 			continue;
309 
310 		TRACE(("  priority: %ld\n", (int32)(priority * 1000)));
311 		if (priority <= bestPriority) {
312 			// the disk system recognized the partition worse than the currently
313 			// best one
314 			module->free_identify_partition_cookie(this, cookie);
315 			continue;
316 		}
317 
318 		// a new winner, replace the previous one
319 		if (bestModule)
320 			bestModule->free_identify_partition_cookie(this, bestCookie);
321 		bestModule = module;
322 		bestCookie = cookie;
323 		bestPriority = priority;
324 	}
325 
326 	// find the best FS module
327 	const file_system_module_info *bestFSModule = NULL;
328 	float bestFSPriority = -1;
329 	for (int32 i = 0; i < sNumFileSystemModules; i++) {
330 		if (sFileSystemModules[i]->identify_file_system == NULL)
331 			continue;
332 
333 		float priority = sFileSystemModules[i]->identify_file_system(this);
334 		if (priority <= 0)
335 			continue;
336 
337 		if (priority > bestFSPriority) {
338 			bestFSModule = sFileSystemModules[i];
339 			bestFSPriority = priority;
340 		}
341 	}
342 
343 	// now let the best matching disk system scan the partition
344 	if (bestModule && bestPriority >= bestFSPriority) {
345 		NodeOpener opener(this, O_RDONLY);
346 		status_t status = bestModule->scan_partition(opener.Descriptor(), this,
347 			bestCookie);
348 		bestModule->free_identify_partition_cookie(this, bestCookie);
349 
350 		if (status != B_OK) {
351 			dprintf("Partitioning module `%s' recognized the partition, but "
352 				"failed to scan it\n", bestModule->pretty_name);
353 			return status;
354 		}
355 
356 		fIsPartitioningSystem = true;
357 
358 		content_type = bestModule->pretty_name;
359 		flags |= B_PARTITION_PARTITIONING_SYSTEM;
360 
361 		// now that we've found something, check our children
362 		// out as well!
363 
364 		NodeIterator iterator = fChildren.GetIterator();
365 		Partition *child = NULL;
366 
367 		while ((child = (Partition *)iterator.Next()) != NULL) {
368 			TRACE(("%p Partition::Scan(): scan child %p (start = %Ld, size "
369 				"= %Ld, parent = %p)!\n", this, child, child->offset,
370 				child->size, child->Parent()));
371 
372 			child->Scan(mountFileSystems);
373 
374 			if (!mountFileSystems || child->IsFileSystem()) {
375 				// move the partitions containing file systems to the partition
376 				// list
377 				fChildren.Remove(child);
378 				gPartitions.Add(child);
379 			}
380 		}
381 
382 		// remove all unused children (we keep only file systems)
383 
384 		while ((child = (Partition *)fChildren.Head()) != NULL) {
385 			fChildren.Remove(child);
386 			delete child;
387 		}
388 
389 		// remember the module name that identified us
390 		fModuleName = bestModule->module.name;
391 
392 		return B_OK;
393 	}
394 
395 	// scan for file systems
396 
397 	if (mountFileSystems) {
398 		// TODO: Use the FS module we've got, if any. Requires to implement the
399 		// identify_file_system() hook in every FS.
400 		return Mount();
401 	}
402 
403 	return B_ENTRY_NOT_FOUND;
404 }
405 
406 }	// namespace boot
407 
408 
409 //	#pragma mark -
410 
411 
412 /*!	Scans the device passed in for partitioning systems. If none are found,
413 	a partition containing the whole device is created.
414 	All created partitions are added to the gPartitions list.
415 */
416 status_t
417 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice)
418 {
419 	TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd,
420 		mountFileSystems ? "yes" : "no"));
421 
422 	Partition *partition = new Partition(fd);
423 
424 	// set some magic/default values
425 	partition->block_size = 512;
426 	partition->size = partition->Size();
427 
428 	// add this partition to the list of partitions, if it contains
429 	// or might contain a file system
430 	if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK
431 			&& partition->IsFileSystem())
432 		|| (!partition->IsPartitioningSystem() && !mountFileSystems)) {
433 		gPartitions.Add(partition);
434 		return B_OK;
435 	}
436 
437 	// if not, we no longer need the partition
438 	delete partition;
439 	return B_OK;
440 }
441 
442 
443 status_t
444 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice)
445 {
446 	TRACE(("add_partitions_for(%p, mountFS = %s)\n", device,
447 		mountFileSystems ? "yes" : "no"));
448 
449 	int fd = open_node(device, O_RDONLY);
450 	if (fd < B_OK)
451 		return fd;
452 
453 	status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice);
454 	if (status < B_OK)
455 		dprintf("add_partitions_for(%d) failed: %ld\n", fd, status);
456 
457 	close(fd);
458 	return B_OK;
459 }
460 
461 
462 partition_data *
463 create_child_partition(partition_id id, int32 index, off_t offset, off_t size,
464 	partition_id childID)
465 {
466 	Partition &partition = *(Partition *)id;
467 	Partition *child = partition.AddChild();
468 	if (child == NULL) {
469 		dprintf("creating partition failed: no memory\n");
470 		return NULL;
471 	}
472 
473 	child->offset = offset;
474 	child->size = size;
475 
476 	// we cannot do anything with the child here, because it was not
477 	// yet initialized by the partition module.
478 	TRACE(("new child partition!\n"));
479 
480 	return child;
481 }
482 
483 
484 partition_data *
485 get_child_partition(partition_id id, int32 index)
486 {
487 	//Partition &partition = *(Partition *)id;
488 
489 	// TODO: do we really have to implement this?
490 	//	The intel partition module doesn't really need this for our mission...
491 	TRACE(("get_child_partition(id = %lu, index = %ld)\n", id, index));
492 
493 	return NULL;
494 }
495 
496 
497 partition_data *
498 get_parent_partition(partition_id id)
499 {
500 	Partition &partition = *(Partition *)id;
501 
502 	return partition.Parent();
503 }
504 
505