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