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
23using 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
35static 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};
50static const int32 sNumPartitionModules = sizeof(sPartitionModules)
51	/ sizeof(partition_module_info *);
52
53/* supported file system modules */
54
55static 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};
72static const int32 sNumFileSystemModules = sizeof(sFileSystemModules)
73	/ sizeof(file_system_module_info *);
74
75extern NodeList gPartitions;
76
77
78namespace boot {
79
80/*! A convenience class to automatically close a
81	file descriptor upon deconstruction.
82*/
83class NodeOpener {
84public:
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
97private:
98	int		fFD;
99};
100
101
102static int32 sIdCounter = 0;
103
104
105//	#pragma mark -
106
107
108Partition::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
125Partition::~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
143Partition *
144Partition::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
166void
167Partition::SetParent(Partition *parent)
168{
169	TRACE(("%p Partition::SetParent %p\n", this, parent));
170	fParent = parent;
171}
172
173
174Partition *
175Partition::Parent() const
176{
177	//TRACE(("%p Partition::Parent is %p\n", this, fParent));
178	return fParent;
179}
180
181
182ssize_t
183Partition::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
198ssize_t
199Partition::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
216off_t
217Partition::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
227int32
228Partition::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
238Partition *
239Partition::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
254status_t
255Partition::_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
294status_t
295Partition::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
312status_t
313Partition::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*/
445status_t
446add_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
475status_t
476add_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
494partition_data *
495create_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
521partition_data *
522get_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
533partition_data *
534get_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