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

Haiku entry_ref: Directory Identity, Stale Names, and Revalidation

Understand Haiku entry_ref as a directory-scoped name, restore BEntry objects safely, and handle rename, deletion, and path changes explicitly.

Haiku’s entry_ref is a compact reference to a directory entry. It contains a device identifier, the parent directory’s inode, and an entry name. That representation is useful for passing a file selection between APIs and storing a reference in a message. It is not an open file descriptor, a lease on the entry, or a guarantee that the same path will continue to name the same object after a rename or deletion.

Understanding the fields prevents a common misconception: an entry_ref does not embed the target file’s inode as a permanent identity. It describes the named entry in a particular directory. If the entry moves, the reference may no longer resolve as expected. Use BEntry to resolve it and check status at the time the operation needs the object.

Create a reference from a checked entry

Start with a BEntry whose construction or SetTo() succeeded. Call GetRef() and check its status before using the result. A reference obtained from a stale BEntry or invalid filesystem operation is not useful simply because its memory fields are populated.

BEntry entry("/boot/home/project/report.txt", false);
status_t status = entry.InitCheck();
if (status != B_OK)
    return status;

entry_ref ref;
status = entry.GetRef(&ref);
if (status != B_OK)
    return status;

BEntry resolved(&ref, false);
status = resolved.InitCheck();
if (status != B_OK)
    return status;

The false traverse argument keeps the link itself in view rather than silently following it. Choose traversal behavior based on the operation. For a file chooser, a link may be an intentional selection; for a recursive cleanup tool, following it can cross a boundary or create a cycle.

When embedding a reference in BMessage, use the typed reference APIs rather than manually serializing the three fields. Typed message fields keep the representation clear and are handled by the platform’s flattening contract. Check the add/find status, and do not trust a message from an external source as proof that the referenced entry exists or is safe to open.

Distinguish entry reference from path string

A path string is a sequence of names interpreted from a root or current directory. An entry_ref includes the parent directory identity, reducing ambiguity if the current working directory changes or multiple directories contain the same name. But it still depends on the name being present in that parent when it is resolved. A reference and a full path have different semantics and should not be interchanged without considering rename and volume changes.

Use BEntry::GetPath() when the caller explicitly needs a display or command-line path. The path can become stale immediately after conversion. Use the entry_ref directly for APIs that accept it, then handle a failure when resolving or opening the entry. If a background operation stores a reference for later, re-resolve and verify the expected type and metadata at execution time.

Do not use an entry_ref as a security token. It does not grant access, bypass filesystem permissions, or prove that the selected object is unchanged. Permissions can change, the entry can be removed, or a new object can appear under a reused name. Check the operation’s result and validate the opened node rather than relying on a prior selection screen.

Handle rename, movement, and volume lifecycle

After renaming or moving an entry, acquire a fresh reference with GetRef() if another subsystem needs the new location. Do not mutate only the name field to guess the result of a filesystem operation: the parent directory or device can change, and the target may not exist. If the application stores references across a session, define what happens when the volume is unmounted, the parent directory is renamed, or the file is deleted.

For durable user preferences, a path may be more comprehensible but also more vulnerable to directory restructuring; a reference may be more precise within a mounted volume but can still become stale. Choose based on the product’s recovery behavior. A recent-files list can skip missing items and offer a relink action. A destructive batch job should stop and ask for review if a stored reference no longer resolves to the expected object.

Comparing two entry_ref values checks their reference fields; it is not a content checksum or proof that file bytes are identical. If content identity matters, open the node and compute an application-level identifier under a defined race policy. If object identity matters while operating, retain a valid node handle for the duration rather than comparing old references later.

Ownership and message boundaries

The name field is dynamically managed by entry_ref constructors, copy operations, assignment, and set_name(). Do not shallow-copy the struct with memcpy or manually free name while the reference object still owns it. Use the value type’s copy/assignment behavior and destroy objects normally. When serializing to a custom format outside BMessage, define an explicit versioned schema rather than dumping struct bytes, which may include pointer and ABI details.

At a process boundary, treat a received reference as a request to resolve an entry, not as trusted state. Validate that it belongs to an expected volume or directory when the application has such a constraint. Check for missing entry, permission failure, and wrong object type. Show a useful message instead of reporting that the application has lost data when a reference is simply stale.

Failure-oriented verification

Test a valid reference, a renamed entry, a removed entry, an unmounted volume, a directory with the same filename on another device, a symlink, and a message carrying a stale reference. Confirm that references are copied without aliasing their name storage and that every resolution checks InitCheck(). Test a path conversion only when a path string is actually needed.

For a file chooser, test that the target can disappear between selection and open. For a background queue, test restart after volume removal and recovery when the user restores the file. For mutation tools, display the resolved target and revalidate immediately before changing it. These checks ensure a BEntry operation reports the current filesystem state rather than an assumption formed earlier.

Make the time between resolution and use as short as the operation allows. A check-then-open sequence can still race another process that renames or replaces the entry; where the API supports operating on an already-open node, keep that node and perform the operation through it. If the workflow must act by name later, show the resolved display name and ask for confirmation when the reference changed. Tests should include a parent directory rename, a move to another volume, deletion followed by recreation at the same spelling, and a remounted volume whose device identity differs.

Do not treat a serialized entry_ref as a durable bookmark across arbitrary system restores or volume replacements. Its device and parent-directory identity fields are meaningful only while they identify the same mounted filesystem objects. For a long-lived user preference, store a separate recovery hint such as a user-selected path or application-level identifier, then ask the user to reconcile it if the reference stops resolving. That hint is not a substitute for checking the entry_ref; it is a way to recover the user’s intent.

In message handling, copy the reference using its value semantics and validate the received fields before resolving. Avoid putting entry_ref bytes into a custom network protocol or raw struct dump because its name storage and ABI are not a portable wire representation. Use the supported message API for local exchange, or define a versioned string/identifier schema for cross-platform data and explicitly map it back to a current entry.

entry_ref is an effective Haiku API boundary for naming a filesystem entry across Kit calls. Treat it as a directory-scoped reference that must be re-resolved, keep its value semantics intact, and define recovery when names or volumes change.

Related:

Sources:

Comments