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