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