Skip to content
Haiku OSHow-To Published Updated 3 min readViews unavailable

How to Create BFS Attributes and Indexes for Fast Haiku Queries

A disciplined BFS metadata workflow: choose typed attributes, create matching volume indexes, write values, query them, and migrate schemas safely.

BFS lets files carry typed attributes and lets each volume maintain indexes over selected attributes. This is why Tracker can treat metadata as first-class data and why queries can find matching files without recursively opening every directory. The performance comes from a contract: attribute name and type must be consistent, and an index must exist on every volume where fast querying is expected.

Define a namespaced schema

Choose a stable attribute name such as DC:project_id, not a generic id likely to collide with another application. Document its data type, encoding, allowed range, whether it is required, and how old versions will be migrated. Attribute display metadata and the on-disk index are related user-experience pieces but are not substitutes for a schema.

Use a native numeric type for sortable numbers and a string type for human-readable identifiers. Writing the digits "100" as text gives string ordering, not integer ordering. Changing a field’s type later under the same name leaves a mixed population that queries cannot interpret coherently.

Create the index on the target volume

BFS indexes are per-volume. Use mkindex on the volume containing the files and specify the matching type. Confirm the exact flags with the installed command’s help because administrative syntax is release-specific:

mkindex -t string DC:project_id /boot/home
lsindex /boot/home | grep 'DC:project_id'

Creating an index does not invent attributes on existing files. Likewise, writing attributes without an index is legal, but a query may need a slower scan or may not behave as the application expects. A deployment procedure should create the index, backfill metadata, verify counts, and only then rely on query-driven UI.

Write attributes with an explicit type

The command-line addattr tool is useful for inspection and migrations. In an application, BNode::WriteAttr() makes the type and byte length explicit:

BNode node("/boot/home/projects/example.task");
const char* value = "project-2026-0042";
ssize_t written = node.WriteAttr("DC:project_id", B_STRING_TYPE,
    0, value, strlen(value) + 1);
if (written != (ssize_t)strlen(value) + 1)
    return B_IO_ERROR;

Check the full byte count, not only a nonnegative result. For updates that span several attributes, define which one marks a complete record; attribute writes are not a multi-field database transaction. A temporary “schema version” or completion field can let readers ignore a partially migrated file.

Inspect results with listattr and catattr, then issue a query through Tracker or the query command. Quote shell metacharacters so the shell does not rewrite the predicate before the query parser sees it. Test exact match, ranges, missing attributes, non-ASCII values, and files moved between volumes.

Operate the index as stored state

Index creation changes a volume and requires appropriate permission. Do not let every app launch race to recreate it. Provide an idempotent installer or administrative action, detect a missing or type-mismatched index, and show a precise repair instruction.

Backups and transfers must preserve extended attributes; a tool that copies only file data and names silently destroys the metadata model. Moving a file to another file system may also lose attributes, and moving it to another BFS volume does not guarantee the destination has your index. Readers need a fallback for unindexed or incomplete records.

For schema changes, introduce a new namespaced attribute, create its index, backfill in bounded batches, verify, switch readers, and retire the old index only after rollback is no longer needed. That procedure preserves query correctness and makes the metadata observable instead of relying on a one-time manual command.

Related:

Sources:

Comments