Skip to content
macOSDeep Dive Published Updated 6 min readViews unavailable

macOS Disk Arbitration: Observing and Approving Mounts Without Racing Finder

How Disk Arbitration models disks, schedules callbacks, requests mounts and ejects, and separates notifications from time-critical approval decisions.

Applications that react to removable media often begin by watching /Volumes. That can reveal a mounted directory, but it misses the decisions that came before it: a whole disk appeared, partitions were discovered, a filesystem was recognized, a mount was proposed, and another client may have approved or rejected the operation. Polling the directory also races Finder and every other process responding to the same device.

Disk Arbitration is macOS’s coordination framework for those events. A client creates a session with the arbitration daemon, registers interest in disks or operations, and schedules callbacks on a run loop or dispatch queue. It can observe lifecycle changes, request mount, unmount, or eject operations, and register an approval callback that may refuse certain actions.

A DADisk is a changing object, not a path

DADiskRef identifies a disk object known to Disk Arbitration. It can represent a whole device or one volume-bearing slice. The BSD device name, media properties, volume name, mount path, protocol, removability, and parent relationships come from a description dictionary rather than from parsing /dev names.

Descriptions are snapshots. A newly appeared disk may not yet have every volume property, and a later description-change callback can report keys that became available or changed. Code should request the keys it needs, validate Core Foundation types, and tolerate absence.

Do not use a friendly volume name as identity. Names can collide and users can change them. The BSD name is useful for the current attachment but can be reused after disconnect. Keep the DADiskRef for the callback lifecycle, and derive any durable identity from the specific media metadata appropriate to the application.

Sessions must be scheduled before callbacks flow

A client begins with DASessionCreate(), registers callbacks, then schedules the session on a CFRunLoop or associates it with a dispatch queue. Scheduling on both is invalid. Choose one concurrency model and make state ownership explicit.

let session = DASessionCreate(kCFAllocatorDefault)!
DASessionSetDispatchQueue(session, workerQueue)

DARegisterDiskAppearedCallback(
    session,
    nil,
    diskAppeared,
    context
)

The matching dictionary passed during registration filters callbacks using disk-description properties. A broad nil match sees more devices but increases work and the chance of reacting to virtual, network, or internal media that the product never intended to manage.

Callbacks should hand off expensive scanning, hashing, or UI work. Blocking the arbitration queue delays other events and may cause an approval deadline to expire. Retain referenced Core Foundation objects according to normal ownership rules when work outlives the callback.

Notification and approval callbacks serve different purposes

An appeared or disappeared callback tells the application what Disk Arbitration observed. A mount-approval callback runs before a proposed mount and returns either NULL to allow it or a DADissenterRef to deny it with a status and optional explanation.

Approval must be quick and based on information already available. It is not the place to upload a file, prompt through a slow network, or scan an entire volume. If policy requires deep inspection, deny or defer the initial mount according to a documented workflow, then perform controlled work and explicitly request the appropriate operation.

Registering for notifications does not grant veto power. Likewise, an approval callback is not a universal device-control mechanism. Apple notes that physically unplugging media bypasses eject approval, because software cannot stop the cable from leaving.

Mount, unmount, and eject requests are asynchronous

Functions such as DADiskMount(), DADiskUnmount(), and DADiskEject() submit a request and invoke a completion callback later. Returning from the function does not mean the operation succeeded. Only the completion callback’s dissenter result establishes success or a refusal reason.

Unmounting a volume and ejecting a whole drive are distinct. A drive with multiple mounted partitions usually needs each consumer quiesced before whole-media eject can complete. Determine the whole-disk relationship through Disk Arbitration instead of trimming a BSD name string.

An application should enter an explicit pending state, disable duplicate UI actions, and wait for completion. If a request fails because a volume is busy, report that fact rather than escalating immediately to a force option. Forced unmount can discard buffered changes.

Matching dictionaries reduce false reactions

Disk Arbitration defines constants for description keys and common match values. Use those constants rather than inventing dictionary strings. A camera importer might match ejectable media and then verify filesystem and content. A disk-management utility may need whole media, while a document app should care only about mountable volumes.

Filters are an efficiency mechanism, not a trust decision. After a match, validate the complete description again. A USB device can report misleading names or identifiers, and a mounted filesystem contains attacker-controlled filenames and metadata.

Store only the minimum state needed to correlate callbacks. Devices can disappear between appearance and a later request. Every operation must handle a stale DADiskRef, a changed mount state, and an application shutdown while work remains queued.

File access still needs filesystem-safe APIs

Once mounted, ordinary file races apply. A path discovered during a scan can be renamed or replaced before it is opened. Use descriptor-based access where possible, avoid following unexpected symlinks, bound file sizes, and treat media content as untrusted.

If the application coordinates documents with other macOS processes, use file coordination where appropriate rather than assuming Disk Arbitration serializes file access. The framework coordinates disk operations. It does not lock every file on the volume or make a partially written document consistent.

Sandboxed and signed applications should test their actual distribution configuration. Disk Arbitration can report the device while sandbox rules still deny file or raw-device access. Do not broaden entitlements simply because a callback arrived.

Build an idempotent device state machine

Real devices generate awkward sequences: a disk appears before its children, descriptions change, a mount fails, a user retries, the cable is removed, and a completion arrives after UI teardown. Model states such as discovered, mount-pending, mounted, unmount-pending, and gone, with transitions that tolerate repetition.

Test multi-partition drives, encrypted volumes, disk images, network volumes, unreadable filesystems, duplicate names, slow media, and physical removal during each operation. Log stable event categories and dissenter status codes, but avoid recording filenames or volume labels unnecessarily.

Disk Arbitration solves the coordination problem only when the application respects its asynchronous model. Schedule one clear callback context, distinguish observation from approval, wait for completion, and assume a device can vanish at every boundary. That produces behavior which cooperates with Finder and the rest of macOS instead of racing them through /Volumes.

Related:

Sources:

Comments