headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / partitions.cpp
blobcfcaaa763928e10c8ebc5b85ea4cd100d2d7935b
1 /*
2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <boot/partitions.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <string.h>
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>
20 #include "RootFileSystem.h"
23 using namespace boot;
25 #define TRACE_PARTITIONS
26 #ifdef TRACE_PARTITIONS
27 # define TRACE(x) dprintf x
28 #else
29 # define TRACE(x) ;
30 #endif
33 /* supported partition modules */
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
50 static const int32 sNumPartitionModules = sizeof(sPartitionModules)
51 / sizeof(partition_module_info *);
53 /* supported file system modules */
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
72 static const int32 sNumFileSystemModules = sizeof(sFileSystemModules)
73 / sizeof(file_system_module_info *);
75 extern NodeList gPartitions;
78 namespace boot {
80 /*! A convenience class to automatically close a
81 file descriptor upon deconstruction.
83 class NodeOpener {
84 public:
85 NodeOpener(Node *node, int mode)
87 fFD = open_node(node, mode);
90 ~NodeOpener()
92 close(fFD);
95 int Descriptor() const { return fFD; }
97 private:
98 int fFD;
102 static int32 sIdCounter = 0;
105 // #pragma mark -
108 Partition::Partition(int fd)
110 fParent(NULL),
111 fIsFileSystem(false),
112 fIsPartitioningSystem(false)
114 TRACE(("%p Partition::Partition\n", this));
116 memset((partition_data *)this, 0, sizeof(partition_data));
118 id = atomic_add(&sIdCounter, 1);
120 // it's safe to close the file
121 fFD = dup(fd);
125 Partition::~Partition()
127 TRACE(("%p Partition::~Partition\n", this));
129 // Tell the children that their parent is gone
131 NodeIterator iterator = gPartitions.GetIterator();
132 Partition *child;
134 while ((child = (Partition *)iterator.Next()) != NULL) {
135 if (child->Parent() == this)
136 child->SetParent(NULL);
139 close(fFD);
143 Partition *
144 Partition::Lookup(partition_id id, NodeList *list)
146 Partition *p;
148 if (list == NULL)
149 list = &gPartitions;
151 NodeIterator iterator = list->GetIterator();
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;
162 return NULL;
166 void
167 Partition::SetParent(Partition *parent)
169 TRACE(("%p Partition::SetParent %p\n", this, parent));
170 fParent = parent;
174 Partition *
175 Partition::Parent() const
177 //TRACE(("%p Partition::Parent is %p\n", this, fParent));
178 return fParent;
182 ssize_t
183 Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
185 if (position > this->size)
186 return 0;
187 if (position < 0)
188 return B_BAD_VALUE;
190 if (position + (off_t)bufferSize > this->size)
191 bufferSize = this->size - position;
193 ssize_t result = read_pos(fFD, this->offset + position, buffer, bufferSize);
194 return result < 0 ? errno : result;
198 ssize_t
199 Partition::WriteAt(void *cookie, off_t position, const void *buffer,
200 size_t bufferSize)
202 if (position > this->size)
203 return 0;
204 if (position < 0)
205 return B_BAD_VALUE;
207 if (position + (off_t)bufferSize > this->size)
208 bufferSize = this->size - position;
210 ssize_t result = write_pos(fFD, this->offset + position, buffer,
211 bufferSize);
212 return result < 0 ? errno : result;
216 off_t
217 Partition::Size() const
219 struct stat stat;
220 if (fstat(fFD, &stat) == B_OK)
221 return stat.st_size;
223 return Node::Size();
227 int32
228 Partition::Type() const
230 struct stat stat;
231 if (fstat(fFD, &stat) == B_OK)
232 return stat.st_mode;
234 return Node::Type();
238 Partition *
239 Partition::AddChild()
241 Partition *child = new(nothrow) Partition(fFD);
242 TRACE(("%p Partition::AddChild %p\n", this, child));
243 if (child == NULL)
244 return NULL;
246 child->SetParent(this);
247 child_count++;
248 fChildren.Add(child);
250 return child;
254 status_t
255 Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
257 TRACE(("%p Partition::_Mount check for file_system: %s\n",
258 this, module->pretty_name));
260 Directory *fileSystem;
261 if (module->get_file_system(this, &fileSystem) == B_OK) {
262 gRoot->AddVolume(fileSystem, this);
263 if (_fileSystem)
264 *_fileSystem = fileSystem;
266 // remember the module that mounted us
267 fModuleName = module->module_name;
268 this->content_type = module->pretty_name;
270 fIsFileSystem = true;
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);
284 fileMapDiskDepth--;
285 #endif
287 return B_OK;
290 return B_BAD_VALUE;
294 status_t
295 Partition::Mount(Directory **_fileSystem, bool isBootDevice)
297 if (isBootDevice && gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE,
298 false)) {
299 return _Mount(&gTarFileSystemModule, _fileSystem);
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;
308 return B_ENTRY_NOT_FOUND;
312 status_t
313 Partition::Scan(bool mountFileSystems, bool isBootDevice)
315 // scan for partitions first (recursively all eventual children as well)
317 TRACE(("%p Partition::Scan()\n", this));
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;
327 const partition_module_info *bestModule = NULL;
328 void *bestCookie = NULL;
329 float bestPriority = -1;
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);
336 TRACE(("check for partitioning_system: %s\n", module->pretty_name));
338 float priority
339 = module->identify_partition(opener.Descriptor(), this, &cookie);
340 if (priority < 0.0)
341 continue;
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;
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;
359 // find the best FS module
360 const 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;
366 float priority = sFileSystemModules[i]->identify_file_system(this);
367 if (priority <= 0)
368 continue;
370 if (priority > bestFSPriority) {
371 bestFSModule = sFileSystemModules[i];
372 bestFSPriority = priority;
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);
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;
389 fIsPartitioningSystem = true;
391 content_type = bestModule->pretty_name;
392 flags |= B_PARTITION_PARTITIONING_SYSTEM;
394 // now that we've found something, check our children
395 // out as well!
397 NodeIterator iterator = fChildren.GetIterator();
398 Partition *child = NULL;
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()));
405 child->Scan(mountFileSystems);
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);
415 // remove all unused children (we keep only file systems)
417 while ((child = (Partition *)fChildren.Head()) != NULL) {
418 fChildren.Remove(child);
419 delete child;
422 // remember the name of the module that identified us
423 fModuleName = bestModule->module.name;
425 return B_OK;
428 // scan for file systems
430 if (mountFileSystems) {
431 // TODO: Use the FS module we've got, if any. Requires to implement the
432 // identify_file_system() hook in every FS.
433 return Mount();
436 return B_ENTRY_NOT_FOUND;
439 } // namespace boot
442 // #pragma mark -
445 /*! Scans the device passed in for partitioning systems. If none are found,
446 a partition containing the whole device is created.
447 All created partitions are added to the gPartitions list.
449 status_t
450 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice)
452 TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd,
453 mountFileSystems ? "yes" : "no"));
455 Partition *partition = new(nothrow) Partition(fd);
457 // set some magic/default values
458 partition->block_size = 512;
459 partition->size = partition->Size();
461 // add this partition to the list of partitions
462 // temporarily for Lookup() to work
463 gPartitions.Add(partition);
465 // keep it, if it contains or might contain a file system
466 if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK
467 && partition->IsFileSystem())
468 || (!partition->IsPartitioningSystem() && !mountFileSystems)) {
469 return B_OK;
472 // if not, we no longer need the partition
473 gPartitions.Remove(partition);
474 delete partition;
475 return B_OK;
479 status_t
480 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice)
482 TRACE(("add_partitions_for(%p, mountFS = %s)\n", device,
483 mountFileSystems ? "yes" : "no"));
485 int fd = open_node(device, O_RDONLY);
486 if (fd < B_OK)
487 return fd;
489 status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice);
490 if (status < B_OK)
491 dprintf("add_partitions_for(%d) failed: %" B_PRIx32 "\n", fd, status);
493 close(fd);
494 return B_OK;
498 partition_data *
499 create_child_partition(partition_id id, int32 index, off_t offset, off_t size,
500 partition_id childID)
502 Partition *partition = Partition::Lookup(id);
503 if (partition == NULL) {
504 dprintf("creating partition failed: could not find partition.\n");
505 return NULL;
508 Partition *child = partition->AddChild();
509 if (child == NULL) {
510 dprintf("creating partition failed: no memory\n");
511 return NULL;
514 child->offset = offset;
515 child->size = size;
517 // we cannot do anything with the child here, because it was not
518 // yet initialized by the partition module.
519 TRACE(("new child partition!\n"));
521 return child;
525 partition_data *
526 get_child_partition(partition_id id, int32 index)
528 // TODO: do we really have to implement this?
529 // The intel partition module doesn't really need this for our mission...
530 TRACE(("get_child_partition(id = %" B_PRId32 ", index = %" B_PRId32 ")\n",
531 id, index));
533 return NULL;
537 partition_data *
538 get_parent_partition(partition_id id)
540 Partition *partition = Partition::Lookup(id);
541 if (partition == NULL) {
542 dprintf("could not find parent partition.\n");
543 return NULL;
545 return partition->Parent();