The AmigaDOS Device List¶
The device list is the master catalog maintained by dos.library of every handler, file system, volume, and assign. Each entry is a DosList structure (defined in dos/dosextens.h), a BCPL-PTR-linked node carrying the name used in absolute paths (the part before the colon) and the MsgPort to which operations on that name are dispatched. Open("DF0:foo"), Read(), Lock(), and CreateNewProc() all consult this list via GetDeviceProc().
The list's head sits in the DosInfo structure (Ch. 17.3). It should not be walked manually — use FindDosEntry() and NextDosEntry() instead.
The DosList structure¶
A DosList is a single struct that covers all four kinds of entries by dol_Type. Three historical alias structures exist (DeviceList, DevInfo, DeviceNode); they overlay the same memory at different field names. The manual recommends using DosList exclusively and ignoring the others.
struct DosList {
BPTR dol_Next; /* BPTR to next entry (ZERO = last) */
LONG dol_Type; /* DLT_DEVICE / DLT_VOLUME / DLT_DIRECTORY / DLT_LATE / DLT_NONBINDING / DLT_PRIVATE */
struct MsgPort *dol_Task; /* port of handler/file system */
BPTR dol_Lock; /* bound lock (assigns only) */
union {
struct { /* dol_Type == DLT_DEVICE */
BSTR dol_Handler;
LONG dol_StackSize;
LONG dol_Priority;
ULONG dol_Startup;
BPTR dol_SegList;
BPTR dol_GlobVec;
} dol_handler;
struct { /* dol_Type == DLT_VOLUME */
struct DateStamp dol_VolumeDate;
BPTR dol_LockList;
LONG dol_DiskType;
APTR dol_NotifyList; /* undocumented */
} dol_volume;
struct { /* dol_Type == DLT_DIRECTORY / DLT_LATE / DLT_NONBINDING */
UBYTE *dol_AssignName;
struct AssignList *dol_List;
} dol_assign;
} dol_misc;
BSTR dol_Name; /* BSTR; NUL-terminated as a courtesy */
};
Entry types¶
dol_Type |
Meaning |
|---|---|
DLT_DEVICE |
File system or handler (e.g., DF0:, CON:, PRT:) |
DLT_VOLUME |
A volume on a mounted medium |
DLT_DIRECTORY |
Regular or multi-assign |
DLT_LATE |
Late-binding assign |
DLT_NONBINDING |
Non-binding assign |
DLT_PRIVATE |
Internal, used to resolve late-binding assigns |
dol_Task semantics vary by type:
- File system / handler:
MsgPortof the handler;NULLif not running (CON-Handler staysNULLbecause each window is its own process). - Volume: port of the underlying FS;
NULLwhile the medium is ejected but locks/handles to the volume are still open (Workbench shows a dimmed icon). - Regular assign: port of the bound FS (first directory of a multi-assign).
- Late-binding assign: initially
NULL, populated on first resolution. - Non-binding assign: always
NULL.
dol_Lock is the lock to the bound directory; it is ZERO for non-binding, late (until bound), and for all non-assign types.
dol_Name is a BSTR; for C convenience dos.library guarantees an extra NUL byte after the BCPL string, so ((UBYTE *)BADDR(dl->dol_Name)) + 1 is a regular C string.
GetDeviceProc — resolving a path to a port¶
struct DevProc *GetDeviceProc(STRPTR name, struct DevProc *prev); /* V36 */
void FreeDeviceProc(struct DevProc *dp); /* V36 */
GetDeviceProc() returns a DevProc describing the next handler/file system candidate for name. Pass prev = NULL on the first call; pass the previous return value to iterate. NULL means "no match" or "scan finished" — in either case, do not call FreeDeviceProc() on it (it already released).
struct DevProc {
struct MsgPort *dvp_Port; /* MsgPort of the candidate handler */
BPTR dvp_Lock; /* dir to use as base for the rest of name */
ULONG dvp_Flags; /* DVPF_ASSIGN (multi-assign candidate), DVPF_UNLOCK (internal) */
struct DosList *dvp_DevNode; /* handler-private, do not touch */
};
The substring of name after the colon (or the whole name, if no colon) is interpreted relative to dvp_Lock on the filesystem reached via dvp_Port. If the file does not exist there and the lock came from a multi-assign, iterate again with the same name to try the next directory.
Important restrictions of GetDeviceProc()¶
dvp_Lockis owned bydos.library— do notUnLock()it, do not pass it toExamine()/ExAll().DupLock()first if you need to keep it.- A
ZEROdvp_Lockmeans the root of the file system (not the boot volume's root) — path resolution then proceeds from there. IoErr()is unreliable on success; check the boolean only.GetDeviceProc()does not handleNIL:,*, or paths relative toCONSOLE:/PROGDIR:reliably; filter these out and useGetConsoleTask()for the console case.- Names longer than 30 characters are truncated — old BCPL limit.
- Assigns can be removed underneath an in-flight
GetDeviceProc(), so locks handed out from assigns may dangle.
Side effects of GetDeviceProc¶
If the matching handler is not loaded, it will be loaded (using dol_Handler path) in the context of the calling process. If dol_Task is NULL, an instance of the handler is started with name as a BSTR in the startup packet. The CON-Handler uses this to know which window to open — historically it opened the window before any packet round-trip, which produced stray windows on early V37 (fixed in V39). Once started, a CON-Handler process remains alive until its last open filehandle is closed; GetDeviceProc() therefore has the side effect of leaving a process around.
Recommendation. Use
GetDeviceProc()only as a preparation step for a subsequent packet send (Ch. 12). Avoid it for handlers you do not intend to communicate with.
Iterating the device list directly¶
For tasks like enumerating mounts or looking up dol_Startup, walk the list yourself.
LockDosList / AttemptLockDosList / UnLockDosList¶
struct DosList *LockDosList(ULONG flags); /* V36, blocks */
struct DosList *AttemptLockDosList(ULONG flags); /* V36, non-blocking */
void UnLockDosList(ULONG flags); /* V36 */
Flags (defined in dos/dosextens.h):
| Flag | Meaning |
|---|---|
LDF_DEVICES |
Handlers and file systems |
LDF_VOLUMES |
Volumes |
LDF_ASSIGNS |
Assigns |
LDF_ALL |
LDF_DEVICES \| LDF_VOLUMES \| LDF_ASSIGNS |
LDF_ENTRY |
Internal only — used by handler startup; do not set |
LDF_DELETE |
Internal only — LDF_WRITE access for removal; do not set |
LDF_READ |
Shared access (must be set with one of the above) |
LDF_WRITE |
Exclusive access (must be set with one of the above) |
LDF_READ and LDF_WRITE are mutually exclusive. NULL return from LockDosList() means the flag combination is invalid; from AttemptLockDosList() it means either invalid flags or "list is currently locked" — the two cannot be distinguished.
Deadlock warning. Handlers and filesystems must not call
LockDosList()from inside packet handlers.dos.librarymay already be holding the list lock from insideGetDeviceProc()while it loads a handler; a handler that then blocks onLockDosList()will deadlock the system. UseAttemptLockDosList(), and if it fails, continue serving packets and retry.
NextDosEntry — step through the list¶
struct DosList *NextDosEntry(struct DosList *dlist, ULONG flags); /* V36 */
Pass the return value of LockDosList()/AttemptLockDosList() (or a previous NextDosEntry()/FindDosEntry()) as dlist. flags should be a subset of the lock flags. Returns the next matching DosList or NULL at end of list.
void ListAllVolumes(void)
{
struct DosList *dl;
dl = LockDosList(LDF_VOLUMES | LDF_READ);
while ((dl = NextDosEntry(dl, LDF_VOLUMES)) != NULL) {
UBYTE *name = ((UBYTE *)BADDR(dl->dol_Name)) + 1;
/* DO NOT print here — printing implies a packet to the
* console, which itself may need the device list locked. */
/* Copy name to a local buffer if you need it later. */
}
UnLockDosList(LDF_VOLUMES | LDF_READ);
}
FindDosEntry — name lookup¶
struct DosList *FindDosEntry(struct DosList *dlist, STRPTR name, ULONG flags); /* V36 */
Search starting at dlist for the next entry whose dol_Name matches name (case-insensitive) and whose dol_Type is in flags. Pass name = NULL to match any. Volumes may share a name (distinguished by dol_VolumeDate); handlers and assigns may not. Pseudo-devices (NIL:, CONSOLE:, *, PROGDIR:) are not on the list and cannot be found here.
Adding and removing entries¶
AddDosEntry¶
LONG AddDosEntry(struct DosList *dlist); /* V36 */
AddDosEntry() acquires LDF_WRITE itself — do not lock the list first. Returns non-zero on success (the DosList is now library-owned and must not be touched); 0 with IoErr() = ERROR_OBJECT_EXISTS on a name clash. Two volumes of the same name are permitted only if their dol_VolumeDate differs.
Use AddDosEntry() for handler/filesystem/volume entries. For assigns use the AssignLock/AssignPath/AssignLate family.
Handlers adding volumes during packet handling must pre-check availability with
AttemptLockDosList()first, otherwise a deadlock withdos.libraryis possible.
RemDosEntry¶
BOOL RemDosEntry(struct DosList *dlist); /* V36 */
RemDosEntry() does not lock the list itself; the caller must hold the appropriate LDF_WRITE lock. Returns non-zero on success; the only failure mode is "entry not on list". Does not release the structure's memory or resources — that is the caller's job. For assigns, prefer AssignLock(name, ZERO).
MakeDosEntry / FreeDosEntry¶
struct DosList *MakeDosEntry(STRPTR name, LONG type); /* V36 */
void FreeDosEntry(struct DosList *dlist); /* V36 */
MakeDosEntry() allocates a DosList and initializes only dol_Type and dol_Name (as a NUL-terminated BSTR); everything else is zero. The caller must fill in the union before AddDosEntry(). Names are recommended to be ≤ 30 chars even though MakeDosEntry() does not enforce it.
FreeDosEntry() releases the structure and its name, nothing more. It is not safe to use freely:
- For
DLT_DEVICEentries:MakeDosNode()(fromexpansion.library) uses a different allocator, soFreeDosEntry()is unsafe; memory leaks are the recommended outcome. - For
DLT_DIRECTORY,DLT_LATE,DLT_NONBINDING: useAssignLock(name, ZERO)instead. - For
DLT_VOLUME: only the managing file system knows how to free the resources it allocated.
Practical application-level use of FreeDosEntry() is essentially zero; the function exists for internal dos.library use.
GetConsoleTask / GetFileSysTask / DeviceProc¶
struct MsgPort *GetConsoleTask(void); /* V36 — port of the caller's console */
struct MsgPort *GetFileSysTask(void); /* V36 — port of the default FS */
struct MsgPort *DeviceProc(STRPTR); /* legacy; prefer GetDeviceProc */
GetConsoleTask() returns the port of the handler responsible for * / CONSOLE: paths of the calling process. Returns NULL for Workbench-launched programs (no console).
GetFileSysTask() returns the port of the default file system — the one used as fallback when resolving paths relative to the ZERO lock. By default this is the boot volume's FS, possibly overridden with SetFileSysTask().
DeviceProc() is the legacy single-shot equivalent of GetDeviceProc(): it does not iterate multi-assigns, does not handle non-binding assigns, and suffers from the same race conditions. It is documented for source-compatibility only; do not use in new code.
See Also¶
- dos.library Reference
- DOS Packets
- DOS Packet Reference
- AmigaDOS Assigns
- BPTR, BSTR, and BCPL Legacy
- IFF Format and Mountlist Reference
Sources: Thomas Richter, "ROM Kernel Reference Manual: AmigaDOS" (First Edition, 2024), Chapter 8 — Administration of Volumes, Devices and Assigns.
Raw: raw/rkm/rkm-dos-book.md
Updated: 2026-10-02