Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

FreeDOS INT 21h/AH=69h: Volume-ID ABI and Read/Write Boundaries

Trace FreeDOS volume-ID function 69h through its IOCTL dispatch and BPB implementation, with a safe GET-only workflow and explicit disk-write hazards.

DOS volume identity has several layers: a drive letter, a filesystem label, a volume serial number, a BIOS drive number, and the physical device are not interchangeable identifiers. FreeDOS exposes a specific interface for reading or setting volume-ID information through INT 21h, function 69h. The GET path is useful for compatibility diagnostics, but the neighboring SET operation is not a harmless metadata query: in the inspected FreeDOS kernel it updates the in-memory drive parameter block and writes the DOS volume’s logical sector zero, which can be a partition’s starting sector rather than physical-disk LBA 0. Keep the two operations separate in code, documentation, and testing.

This guide follows the FreeDOS kernel implementation at a pinned upstream commit so that register behavior and write consequences can be reviewed against exact source. It focuses on AH=69h dispatch and its block-device IOCTL bridge, not the generic IOCTL family or shell-level VOL/LABEL commands.

The register-level entry point

At the inspected kernel revision, INT 21h dispatches AH=69h to the volume-ID path. AL selects the operation: 0 requests information, 1 requests a set, and other values take the invalid-function path. BL selects the drive according to the implementation: zero means the current default drive; a nonzero value is converted from the DOS one-based drive numbering by subtracting one. The handler checks the drive structures before issuing the internal device-control request.

The handler preserves and rewrites register fields before calling DosDevIOctl. In the source snapshot, it selects control codes 0x0866 for GET and 0x0846 for SET, sets AL to 0x0D, and passes the user register frame to the IOCTL path. These values describe the inspected FreeDOS implementation; do not copy them as an undocumented universal contract for every DOS-compatible kernel. If a program must support other DOS variants, verify their documented API and test the exact target rather than assuming FreeDOS internals are identical.

The API uses a Gioc_media structure containing an IOCTL level, a 32-bit serial number, an 11-byte volume label, and an 8-byte filesystem-type field. The field widths matter: the label is a fixed-width byte array, not a NUL-terminated C string guaranteed by this structure. Programs should copy it into a larger local buffer, trim/pad only according to the format they support, and avoid reading beyond the declared 11 bytes.

Follow the GET path into the drive parameter block

The FreeDOS disk IOCTL handler treats function 0x66 as the GET operation. It obtains the drive’s current BPB/drive parameter information and copies the serial number, volume label, and filesystem type into the returned structure. Those values are metadata associated with the filesystem/drive representation maintained by the kernel. They are not a unique physical-device identity, cryptographic identifier, or proof that a removable device is the same hardware seen earlier.

This is why a read should be described precisely: “the kernel returned this volume-ID record for DOS drive D:” is supportable; “this serial number identifies the physical disk” is not. Drive letters can be reassigned, filesystem metadata can be cloned, and duplicated volume identifiers can exist. A management tool that needs physical identity should use a separately documented platform interface and should not elevate this field into an authentication factor.

A conceptual GET-only call looks like this in assembly-oriented pseudocode:

; Conceptual FreeDOS INT 21h/AH=69h GET request only.
; DS:DX points to a correctly laid out Gioc_media buffer.
mov     ah, 69h
mov     al, 00h          ; GET volume ID
mov     bl, 00h          ; current default drive
mov     dx, offset media_info
int     21h
jc      get_failed      ; inspect documented/target-specific status

This illustration is intentionally limited to the GET selector and does not provide a SET example. The pointer and record layout must match the selected DOS ABI and memory model; a high-level-language structure may require packing and far-pointer handling. Test the compiled binary against the target’s documentation and emulator/device behavior. Never infer success solely from a buffer that happens to contain plausible bytes.

Why SET crosses a destructive boundary

The same top-level API has AL=1 for SET. In FreeDOS’s disk implementation, the setter checks the extended BPB signature before changing serial/label data. The inspected code recognizes signature values 0x29 and 0x28 in the relevant extended BPB location; it updates the serial field and updates the volume label when the format supports that field. It then calls the volume-sector-zero write path (RWzero with LBA_WRITE). The drive’s partition offset is included for partition-backed devices, so this is a filesystem metadata write to the volume’s logical sector zero, not necessarily physical-disk LBA 0 and not an in-memory-only preference.

The precise layout varies with FAT BPB form and the implementation branches on fields such as the total-sector representation to locate the extended signature. The article’s operational takeaway is broader and safer than trying to hand-edit offsets: SET can modify the on-disk logical sector zero of the selected DOS volume, including a partition’s boot sector. A wrong drive, wrong pointer, stale image mapping, or unsupported filesystem layout can damage filesystem metadata or the volume’s ability to mount.

Do not run a SET experiment on a host’s current disk, a mounted shared image, a production VM, or a removable device you care about. This article intentionally omits a SET invocation. If compatibility research truly requires exercising it, use a disposable, backed-up disk image in an isolated emulator, snapshot the image, verify which emulated drive is selected, and compare the before/after image offline. No destructive example is needed to understand the ABI boundary.

Error and compatibility handling

The kernel’s handler distinguishes unsupported AL values and unavailable drive structures before the IOCTL work. Applications should treat failure as a normal result: capture flags/register status as specified for the target, validate all input and output pointers, and do not assume every drive supports the same filesystem extensions. The pinned implementation is evidence about FreeDOS, not a claim that a network redirector, RAM disk, optical device, or third-party block driver will return identical metadata.

When diagnosing a mismatch, compare the exact DOS drive mapping, the raw GET buffer, the volume label visible to DOS utilities, and the filesystem image’s BPB fields in a read-only copy. If these disagree, identify the first boundary where they diverge. Do not “repair” the label by sending SET until you have established the filesystem type, backed up the image, and obtained explicit approval for a write operation.

A safe validation plan

For a compatibility test, begin with an immutable test image and a known FreeDOS build. Record the kernel commit/build, emulator version, drive-letter map, image hash, and test program. Perform only the GET operation, save the returned bytes, and verify field widths and byte ordering against the source structure. Repeat after remapping the same image to another DOS letter to demonstrate that the drive letter is a selector, not identity. Compare results with offline BPB inspection, but do not mount the reference image read-write during this check.

For negative tests, use an invalid drive selector or an unsupported operation only in a disposable emulator where the result cannot alter real storage. Confirm that the program detects the error rather than treating stale buffer contents as success. If multi-kernel support is required, maintain a matrix of documented behavior per kernel and version. Avoid undocumented SET tests unless a separate test plan deliberately isolates and snapshots every write target.

The essential engineering boundary is simple: AH=69h exposes a fixed record and a GET path suitable for observation, while SET in this FreeDOS implementation reaches a disk-write operation. Treat volume serials and labels as filesystem metadata, validate ABI details against the target kernel, and keep normal diagnostics read-only.

Related:

Sources:

Comments