* Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <boot/partitions.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <boot/FileMapDisk.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <boot/stdio.h>
#include <boot/vfs.h>
#include <ddm_modules.h>
#include "RootFileSystem.h"
using namespace boot;
#define TRACE_PARTITIONS
#ifdef TRACE_PARTITIONS
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static const partition_module_info *sPartitionModules[] = {
#ifdef BOOT_SUPPORT_PARTITION_AMIGA
&gAmigaPartitionModule,
#endif
#ifdef BOOT_SUPPORT_PARTITION_EFI
&gEFIPartitionModule,
#endif
#ifdef BOOT_SUPPORT_PARTITION_INTEL
&gIntelPartitionMapModule,
&gIntelExtendedPartitionModule,
#endif
#ifdef BOOT_SUPPORT_PARTITION_APPLE
&gApplePartitionModule,
#endif
};
static const int32 sNumPartitionModules = sizeof(sPartitionModules)
/ sizeof(partition_module_info *);
static file_system_module_info *sFileSystemModules[] = {
#ifdef BOOT_SUPPORT_FILE_SYSTEM_BFS
&gBFSFileSystemModule,
#endif
#ifdef BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
&gAmigaFFSFileSystemModule,
#endif
#ifdef BOOT_SUPPORT_FILE_SYSTEM_FAT
&gFATFileSystemModule,
#endif
#ifdef BOOT_SUPPORT_FILE_SYSTEM_HFS_PLUS
&gHFSPlusFileSystemModule,
#endif
#ifdef BOOT_SUPPORT_FILE_SYSTEM_TARFS
&gTarFileSystemModule,
#endif
};
static const int32 sNumFileSystemModules = sizeof(sFileSystemModules)
/ sizeof(file_system_module_info *);
extern NodeList gPartitions;
namespace boot {
file descriptor upon deconstruction.
*/
class NodeOpener {
public:
NodeOpener(Node *node, int mode)
{
fFD = open_node(node, mode);
}
~NodeOpener()
{
close(fFD);
}
int Descriptor() const { return fFD; }
private:
int fFD;
};
static int32 sIdCounter = 0;
Partition::Partition(int fd)
:
fParent(NULL),
fIsFileSystem(false),
fIsPartitioningSystem(false)
{
TRACE(("%p Partition::Partition\n", this));
memset((partition_data *)this, 0, sizeof(partition_data));
id = atomic_add(&sIdCounter, 1);
fFD = dup(fd);
}
Partition::~Partition()
{
TRACE(("%p Partition::~Partition\n", this));
NodeIterator iterator = gPartitions.GetIterator();
Partition *child;
while ((child = (Partition *)iterator.Next()) != NULL) {
if (child->Parent() == this)
child->SetParent(NULL);
}
close(fFD);
}
Partition *
Partition::Lookup(partition_id id, NodeList *list)
{
Partition *p;
if (list == NULL)
list = &gPartitions;
NodeIterator iterator = list->GetIterator();
while ((p = (Partition *)iterator.Next()) != NULL) {
if (p->id == id)
return p;
if (!p->fChildren.IsEmpty()) {
Partition *c = Lookup(id, &p->fChildren);
if (c)
return c;
}
}
return NULL;
}
void
Partition::SetParent(Partition *parent)
{
TRACE(("%p Partition::SetParent %p\n", this, parent));
fParent = parent;
}
Partition *
Partition::Parent() const
{
return fParent;
}
ssize_t
Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
{
if (position > this->size)
return 0;
if (position < 0)
return B_BAD_VALUE;
if (position + (off_t)bufferSize > this->size)
bufferSize = this->size - position;
ssize_t result = read_pos(fFD, this->offset + position, buffer, bufferSize);
return result < 0 ? errno : result;
}
ssize_t
Partition::WriteAt(void *cookie, off_t position, const void *buffer,
size_t bufferSize)
{
if (position > this->size)
return 0;
if (position < 0)
return B_BAD_VALUE;
if (position + (off_t)bufferSize > this->size)
bufferSize = this->size - position;
ssize_t result = write_pos(fFD, this->offset + position, buffer,
bufferSize);
return result < 0 ? errno : result;
}
off_t
Partition::Size() const
{
struct stat stat;
if (fstat(fFD, &stat) == B_OK)
return stat.st_size;
return Node::Size();
}
int32
Partition::Type() const
{
struct stat stat;
if (fstat(fFD, &stat) == B_OK)
return stat.st_mode;
return Node::Type();
}
Partition *
Partition::AddChild()
{
Partition *child = new(nothrow) Partition(fFD);
TRACE(("%p Partition::AddChild %p\n", this, child));
if (child == NULL)
return NULL;
child->SetParent(this);
child_count++;
fChildren.Add(child);
return child;
}
status_t
Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
{
TRACE(("%p Partition::_Mount check for file_system: %s\n",
this, module->pretty_name));
Directory *fileSystem;
if (module->get_file_system(this, &fileSystem) == B_OK) {
gRoot->AddVolume(fileSystem, this);
if (_fileSystem)
*_fileSystem = fileSystem;
fModuleName = module->module_name;
this->content_type = module->pretty_name;
fIsFileSystem = true;
#ifdef BOOT_SUPPORT_FILE_MAP_DISK
static int fileMapDiskDepth = 0;
if (!fileMapDiskDepth++) {
FileMapDisk *disk = FileMapDisk::FindAnyFileMapDisk(fileSystem);
if (disk) {
TRACE(("%p Partition::_Mount: found FileMapDisk\n", this));
disk->RegisterFileMapBootItem();
add_partitions_for(disk, true, false);
}
}
fileMapDiskDepth--;
#endif
return B_OK;
}
return B_BAD_VALUE;
}
status_t
Partition::Mount(Directory **_fileSystem, bool isBootDevice)
{
if (isBootDevice && gBootParams.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE,
false)) {
return _Mount(&gTarFileSystemModule, _fileSystem);
}
for (int32 i = 0; i < sNumFileSystemModules; i++) {
status_t status = _Mount(sFileSystemModules[i], _fileSystem);
if (status == B_OK)
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
status_t
Partition::Scan(bool mountFileSystems, bool isBootDevice)
{
TRACE(("%p Partition::Scan()\n", this));
if (isBootDevice && gBootParams.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE,
false)) {
return B_ENTRY_NOT_FOUND;
}
const partition_module_info *bestModule = NULL;
void *bestCookie = NULL;
float bestPriority = -1;
for (int32 i = 0; i < sNumPartitionModules; i++) {
const partition_module_info *module = sPartitionModules[i];
void *cookie = NULL;
NodeOpener opener(this, O_RDONLY);
TRACE(("check for partitioning_system: %s\n", module->pretty_name));
float priority
= module->identify_partition(opener.Descriptor(), this, &cookie);
if (priority < 0.0)
continue;
TRACE((" priority: %" B_PRId32 "\n", (int32)(priority * 1000)));
if (priority <= bestPriority) {
module->free_identify_partition_cookie(this, cookie);
continue;
}
if (bestModule)
bestModule->free_identify_partition_cookie(this, bestCookie);
bestModule = module;
bestCookie = cookie;
bestPriority = priority;
}
file_system_module_info *bestFSModule = NULL;
float bestFSPriority = -1;
for (int32 i = 0; i < sNumFileSystemModules; i++) {
if (sFileSystemModules[i]->identify_file_system == NULL)
continue;
float priority = sFileSystemModules[i]->identify_file_system(this);
if (priority <= 0)
continue;
if (priority > bestFSPriority) {
bestFSModule = sFileSystemModules[i];
bestFSPriority = priority;
}
}
if (bestModule && bestPriority >= bestFSPriority) {
NodeOpener opener(this, O_RDONLY);
status_t status = bestModule->scan_partition(opener.Descriptor(), this,
bestCookie);
bestModule->free_identify_partition_cookie(this, bestCookie);
if (status != B_OK) {
dprintf("Partitioning module `%s' recognized the partition, but "
"failed to scan it\n", bestModule->pretty_name);
return status;
}
fIsPartitioningSystem = true;
content_type = bestModule->pretty_name;
flags |= B_PARTITION_PARTITIONING_SYSTEM;
NodeIterator iterator = fChildren.GetIterator();
Partition *child = NULL;
while ((child = (Partition *)iterator.Next()) != NULL) {
TRACE(("%p Partition::Scan(): scan child %p (start = %" B_PRIdOFF
", size = %" B_PRIdOFF ", parent = %p)!\n", this, child,
child->offset, child->size, child->Parent()));
child->Scan(mountFileSystems);
if (!mountFileSystems || child->IsFileSystem()) {
fChildren.Remove(child);
gPartitions.Add(child);
}
}
while ((child = (Partition *)fChildren.Head()) != NULL) {
fChildren.Remove(child);
delete child;
}
fModuleName = bestModule->module.name;
return B_OK;
}
if (mountFileSystems && bestFSModule != NULL)
return _Mount(bestFSModule, NULL);
return B_ENTRY_NOT_FOUND;
}
}
a partition containing the whole device is created.
All created partitions are added to the gPartitions list.
*/
status_t
add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice)
{
TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd,
mountFileSystems ? "yes" : "no"));
Partition *partition = new(nothrow) Partition(fd);
partition->block_size = 512;
partition->size = partition->Size();
gPartitions.Add(partition);
if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK
&& partition->IsFileSystem())
|| (!partition->IsPartitioningSystem() && !mountFileSystems)) {
return B_OK;
}
gPartitions.Remove(partition);
delete partition;
return B_OK;
}
status_t
add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice)
{
TRACE(("add_partitions_for(%p, mountFS = %s)\n", device,
mountFileSystems ? "yes" : "no"));
int fd = open_node(device, O_RDONLY);
if (fd < B_OK)
return fd;
status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice);
if (status < B_OK)
dprintf("add_partitions_for(%d) failed: %" B_PRIx32 "\n", fd, status);
close(fd);
return B_OK;
}
partition_data *
create_child_partition(partition_id id, int32 index, off_t offset, off_t size,
partition_id childID)
{
Partition *partition = Partition::Lookup(id);
if (partition == NULL) {
dprintf("creating partition failed: could not find partition.\n");
return NULL;
}
Partition *child = partition->AddChild();
if (child == NULL) {
dprintf("creating partition failed: no memory\n");
return NULL;
}
child->offset = offset;
child->size = size;
TRACE(("new child partition!\n"));
return child;
}
partition_data *
get_child_partition(partition_id id, int32 index)
{
TRACE(("get_child_partition(id = %" B_PRId32 ", index = %" B_PRId32 ")\n",
id, index));
return NULL;
}
partition_data *
get_parent_partition(partition_id id)
{
Partition *partition = Partition::Lookup(id);
if (partition == NULL) {
dprintf("could not find parent partition.\n");
return NULL;
}
return partition->Parent();
}