Skip to content
Haiku OSDeep Dive Published Updated 6 min readViews unavailable

Haiku's Private Disk-Device Roster: Read-Only Partition Inventory

Inspect Haiku's disk and partition tree with the private Disk Device Kit while separating it from public mounted-volume APIs and destructive operations.

Haiku has two different storage questions that are easy to conflate: “which file systems are mounted?” and “what disk and partition objects does the system currently know about?” The public BVolumeRoster is the supported starting point for the first question. The lower-level BDiskDeviceRoster and BPartition interfaces model devices and nested partition objects, but their headers live under Haiku’s headers/private/storage tree. Treat them as implementation-facing interfaces, not as a stable public application ABI.

That boundary is the most important part of a safe inventory tool. A read-only diagnostic can use the private roster to inspect the current hierarchy, but it should not silently grow into a partition editor, mount controller, or long-term identity database. The source details below were checked against Haiku revision 3a93a9f6a03f12e89e7a700f41fc4f696e3709e8 on 2026-10-12. A later Haiku revision can change private interfaces without preserving compatibility.

Choose the roster that matches the question

BVolumeRoster enumerates mounted volumes. A volume is not a physical disk: one disk may contain several partitions, an unmounted partition does not appear as a mounted volume, and a mounted file system does not by itself describe the device layout beneath it. The existing volume-roster workflow remains the right choice for a file browser, backup tool, or application that only needs mounted roots.

BDiskDeviceRoster instead provides GetNextDevice() and partition-visiting helpers. BDiskDevice derives from BPartition, so a device and its child partitions can be traversed through the same hierarchy API. In the inspected header, BPartition exposes getters such as Offset(), Size(), Status(), Flags(), Name(), Type(), ContentType(), CountChildren(), and ChildAt(). It also exposes operations that can change state, including mount, unmount, initialize, resize, move, and child creation or deletion. An inventory utility should deliberately call only getters.

Make one checked pass over the current tree

The following is the traversal core for a source-tree diagnostic built against the matching Haiku revision. It is not a promise that a packaged third-party application can include these private headers on every release. It prints numeric metadata and intentionally avoids writing to a device or mounting a partition.

#include <stdio.h>

#include <DiskDevice.h>
#include <DiskDeviceRoster.h>
#include <Partition.h>

static void
PrintPartition(const BPartition& partition, int depth)
{
	printf("%*s id=%d index=%d offset=%lld size=%lld status=%u flags=0x%x\n",
		depth * 2, "", (int)partition.ID(), (int)partition.Index(),
		(long long)partition.Offset(), (long long)partition.Size(),
		(unsigned)partition.Status(), (unsigned)partition.Flags());

	const int32 childCount = partition.CountChildren();
	for (int32 i = 0; i < childCount; ++i) {
		BPartition* child = partition.ChildAt(i);
		if (child != NULL)
			PrintPartition(*child, depth + 1);
	}
}

int
InventoryKnownDevices()
{
	BDiskDeviceRoster roster;
	BDiskDevice device;
	status_t status;

	while ((status = roster.GetNextDevice(&device)) == B_OK)
		PrintPartition(device, 0); // Consume this tree before reusing device.

	if (status == B_ENTRY_NOT_FOUND)
		return 0; // Normal end of the current enumeration.

	// Preserve the actual status in the caller's diagnostic/logging path.
	return (int)status;
}

The current roster implementation documents B_ENTRY_NOT_FOUND as the normal end of the list and returns other errors for failures. It rejects a null destination with B_BAD_VALUE; therefore, do not treat every non-B_OK status as “there were no disks.” In production diagnostics, record the unexpected status and mark the inventory incomplete instead of publishing a partial tree as authoritative.

ChildAt() returns a pointer owned by the partition hierarchy. The sample reads through that pointer and never deletes it. It also prints each tree before the next GetNextDevice() call reuses the BDiskDevice object. If the application needs to retain a result, copy the fields it needs into its own value objects and document that they are observations from one pass, not live handles or permanent IDs.

Interpret fields as observations, not identity

Offset() and Size() are byte counts reported by the partition model. The hierarchy can include nested, hidden, or virtual objects, so do not flatten it and assume every child corresponds to a directly addressable on-disk partition. Preserve parent/child relationships and record which device each observation came from. Name() is allowed to be null, and content information can be unavailable or uninitialized; represent that as “unknown” rather than inventing a label or treating a missing field as zero.

Haiku’s DiskDeviceDefs.h distinguishes partition status from flags. Status values include valid, corrupt, unrecognized, and uninitialized states. Flags describe properties such as device, file system, partitioning system, read-only, mounted, and busy. Do not derive a mount point from a display name or conclude that a partition is safe to modify merely because it is not marked busy. The disk-system implementation and the current device state govern what operations are supported.

Most importantly, BPartition::ID() is not a persistent identity across reboot. The current source explicitly describes it as a unique identifier that in general will not be the same after reboot. It is suitable for correlating objects in the current system context, not for a backup catalog key, cross-boot database, or synchronization protocol. A mount path and human-readable name are also mutable. If an application must match media over time, define and validate an application-level identity strategy and surface ambiguity instead of silently binding a saved record to a different device.

Keep refresh and mutation paths separate

The private roster header exposes StartWatching() and StopWatching() along with event masks for device lists, partitions, mount state, and other changes. A watcher can prompt a fresh enumeration, but an event should not be treated as a complete transactional snapshot. Make reconciliation idempotent: collect a complete new pass, distinguish failure from absence, and only then update the displayed model. If the API contract for a particular event field matters, read the matching protocol documentation at the exact source revision before relying on it.

Do not call Mount(), Unmount(), Initialize(), Repair(), Resize(), Move(), CreateChild(), or DeleteChild() from a diagnostic path. The private class makes some of these operations convenient, but they can change file-system or partition state and are not required for inventory. A safer design keeps the report command read-only and gives any maintenance operation a separate user-reviewed flow with a verified target, explicit confirmation, backup policy, and checked status result.

If the goal is ordinary end-user functionality rather than Haiku development or system diagnostics, stay with public APIs such as BVolumeRoster. If private partition topology is essential, pin the Haiku revision, build and test against that revision’s headers, check the target SDK, and expect to revalidate the interface on each upgrade. A source-level code review is not a substitute for exercising removable media, virtual devices, nested partition maps, and error paths on the supported Haiku releases.

A practical validation matrix

Test with no removable media, one disk with several partitions, an unmounted partition, a read-only device, a file-backed virtual device if the target setup supports one, and a device removed during refresh. Confirm that end-of-list differs from an unexpected status, that null names and content types render as unknown, and that a mid-pass failure does not erase the last known-good inventory. Escape names before displaying them in a terminal or log; storage labels are data, not trusted formatting instructions.

This private roster can answer a narrower question than BVolumeRoster, but the answer remains a snapshot from a system implementation detail. Keep the public/private boundary explicit, copy only the metadata needed, reconcile after change notifications, and make it impossible for an inventory path to issue an accidental storage mutation.

Related:

Sources:

Comments