2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include <boot/partitions.h>
13 #include <boot/FileMapDisk.h>
14 #include <boot/platform.h>
15 #include <boot/stage2.h>
16 #include <boot/stdio.h>
18 #include <ddm_modules.h>
20 #include "RootFileSystem.h"
25 #define TRACE_PARTITIONS
26 #ifdef TRACE_PARTITIONS
27 # define TRACE(x) dprintf x
33 /* supported partition modules */
35 static const partition_module_info
*sPartitionModules
[] = {
36 #ifdef BOOT_SUPPORT_PARTITION_AMIGA
37 &gAmigaPartitionModule
,
39 #ifdef BOOT_SUPPORT_PARTITION_EFI
42 #ifdef BOOT_SUPPORT_PARTITION_INTEL
43 &gIntelPartitionMapModule
,
44 &gIntelExtendedPartitionModule
,
46 #ifdef BOOT_SUPPORT_PARTITION_APPLE
47 &gApplePartitionModule
,
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
,
59 #ifdef BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
60 &gAmigaFFSFileSystemModule
,
62 #ifdef BOOT_SUPPORT_FILE_SYSTEM_FAT
63 &gFATFileSystemModule
,
65 #ifdef BOOT_SUPPORT_FILE_SYSTEM_HFS_PLUS
66 &gHFSPlusFileSystemModule
,
68 #ifdef BOOT_SUPPORT_FILE_SYSTEM_TARFS
69 &gTarFileSystemModule
,
72 static const int32 sNumFileSystemModules
= sizeof(sFileSystemModules
)
73 / sizeof(file_system_module_info
*);
75 extern NodeList gPartitions
;
80 /*! A convenience class to automatically close a
81 file descriptor upon deconstruction.
85 NodeOpener(Node
*node
, int mode
)
87 fFD
= open_node(node
, mode
);
95 int Descriptor() const { return fFD
; }
102 static int32 sIdCounter
= 0;
108 Partition::Partition(int fd
)
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
125 Partition::~Partition()
127 TRACE(("%p Partition::~Partition\n", this));
129 // Tell the children that their parent is gone
131 NodeIterator iterator
= gPartitions
.GetIterator();
134 while ((child
= (Partition
*)iterator
.Next()) != NULL
) {
135 if (child
->Parent() == this)
136 child
->SetParent(NULL
);
144 Partition::Lookup(partition_id id
, NodeList
*list
)
151 NodeIterator iterator
= list
->GetIterator();
153 while ((p
= (Partition
*)iterator
.Next()) != NULL
) {
156 if (!p
->fChildren
.IsEmpty()) {
157 Partition
*c
= Lookup(id
, &p
->fChildren
);
167 Partition::SetParent(Partition
*parent
)
169 TRACE(("%p Partition::SetParent %p\n", this, parent
));
175 Partition::Parent() const
177 //TRACE(("%p Partition::Parent is %p\n", this, fParent));
183 Partition::ReadAt(void *cookie
, off_t position
, void *buffer
, size_t bufferSize
)
185 if (position
> this->size
)
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
;
199 Partition::WriteAt(void *cookie
, off_t position
, const void *buffer
,
202 if (position
> this->size
)
207 if (position
+ (off_t
)bufferSize
> this->size
)
208 bufferSize
= this->size
- position
;
210 ssize_t result
= write_pos(fFD
, this->offset
+ position
, buffer
,
212 return result
< 0 ? errno
: result
;
217 Partition::Size() const
220 if (fstat(fFD
, &stat
) == B_OK
)
228 Partition::Type() const
231 if (fstat(fFD
, &stat
) == B_OK
)
239 Partition::AddChild()
241 Partition
*child
= new(nothrow
) Partition(fFD
);
242 TRACE(("%p Partition::AddChild %p\n", this, child
));
246 child
->SetParent(this);
248 fChildren
.Add(child
);
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);
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
);
279 TRACE(("%p Partition::_Mount: found FileMapDisk\n", this));
280 disk
->RegisterFileMapBootItem();
281 add_partitions_for(disk
, true, false);
295 Partition::Mount(Directory
**_fileSystem
, bool isBootDevice
)
297 if (isBootDevice
&& gBootVolume
.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE
,
299 return _Mount(&gTarFileSystemModule
, _fileSystem
);
302 for (int32 i
= 0; i
< sNumFileSystemModules
; i
++) {
303 status_t status
= _Mount(sFileSystemModules
[i
], _fileSystem
);
308 return B_ENTRY_NOT_FOUND
;
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
322 if (isBootDevice
&& gBootVolume
.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE
,
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
];
334 NodeOpener
opener(this, O_RDONLY
);
336 TRACE(("check for partitioning_system: %s\n", module
->pretty_name
));
339 = module
->identify_partition(opener
.Descriptor(), this, &cookie
);
343 TRACE((" priority: %" B_PRId32
"\n", (int32
)(priority
* 1000)));
344 if (priority
<= bestPriority
) {
345 // the disk system recognized the partition worse than the currently
347 module
->free_identify_partition_cookie(this, cookie
);
351 // a new winner, replace the previous one
353 bestModule
->free_identify_partition_cookie(this, bestCookie
);
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
)
366 float priority
= sFileSystemModules
[i
]->identify_file_system(this);
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,
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
);
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
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
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
);
422 // remember the name of the module that identified us
423 fModuleName
= bestModule
->module
.name
;
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.
436 return B_ENTRY_NOT_FOUND
;
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.
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
)) {
472 // if not, we no longer need the partition
473 gPartitions
.Remove(partition
);
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
);
489 status_t status
= add_partitions_for(fd
, mountFileSystems
, isBootDevice
);
491 dprintf("add_partitions_for(%d) failed: %" B_PRIx32
"\n", fd
, status
);
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");
508 Partition
*child
= partition
->AddChild();
510 dprintf("creating partition failed: no memory\n");
514 child
->offset
= offset
;
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"));
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",
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");
545 return partition
->Parent();