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