Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

DOS INT 21h AH=57h: Preserve File Times Through the Handle Lifecycle

Use DOS's handle-based file-time API with correct CX/DX ordering, checked carry flags, write-before-set sequencing, and close/reopen verification on FreeDOS.

DOS’s classic file date/time service is an operation on an open handle. INT 21h with AH=57h can retrieve or assign a file’s packed last-write values, but a successful call is only one step in a metadata-preservation workflow. Writes, buffered state, close processing, handle ownership, and storage failures all affect whether the intended value survives.

The FreeDOS kernel provides a useful primary implementation record. Its interrupt dispatcher maps AL=00h to retrieval and AL=01h to assignment, with BX identifying the handle, CX holding time, and DX holding date. The examples below target that classic interface. They do not claim support for long-filename extensions, creation-time APIs, or identical behavior on every DOS redirector.

Keep the register contract explicit

The two core operations are:

Get: AX=5700h, BX=open handle
     on success: CX=packed time, DX=packed date

Set: AX=5701h, BX=open handle
     CX=packed time, DX=packed date

Check CF after each INT 21h call.
On an error path, AX carries the DOS error result.

The date/time ordering is easy to reverse because both values fit in 16 bits. Name stored variables saved_time and saved_date, and keep the CX/DX assignment adjacent to the call. An ambiguous pair such as word1 and word2 invites a defect that may produce plausible-looking but incorrect metadata.

Do not use AX as the only success test. DOS interrupt services use the carry flag to distinguish an error result from a successful return contract. Preserve or examine it before another instruction that changes flags. If the operation fails, record the call, handle, and returned error before cleanup obscures the original cause.

Retrieve from an already owned handle

The following NASM-style fragment assumes a real-mode DOS program, a valid data segment, and a regular file handle already stored in file_handle. It is a fragment, not a standalone executable:

    mov bx, [file_handle]
    mov ax, 5700h
    int 21h
    jc  get_time_failed
    mov [saved_time], cx
    mov [saved_date], dx

The FreeDOS retrieval function resolves the handle to its System File Table entry and returns that entry’s date and time. An invalid handle produces an invalid-handle result. This establishes an important boundary: the service retrieves the file state associated with the live DOS handle, not an arbitrary raw directory sector supplied by the caller.

Avoid passing standard handles merely because they are small valid integers. A redirected standard handle might reference a file, while a console handle refers to a device. A metadata-preserving copy routine should operate on explicitly opened source and destination files and retain clear ownership of their handles.

Understand what the classic values represent

Classic FAT last-write fields encode a date and a time without a timezone offset. The time has two-second granularity, and the date uses an offset from 1980. This API exposes packed values rather than a portable UTC instant. Copying them preserves the DOS-visible representation, not a timezone history that was never stored in those fields.

Microsoft’s FAT specification documents the separate on-disk creation, access, and modification fields. The classic 5700h/5701h contract discussed here is not a promise to preserve all those fields. A program that reports “all timestamps preserved” after using only this interface overstates its work.

When both volumes and clocks use the same interpretation, retaining the packed last-write words can be useful for archival copies and reproducible legacy builds. When exporting to a timezone-aware host filesystem, record the conversion policy separately. An apparent one-hour difference can arise from interpretation; do not rewrite the original words before preserving the evidence.

Write first, set the final time last

FreeDOS’s assignment routine marks an explicit date/time flag in the System File Table and records the supplied words. The FAT write path clears that explicit-time state when it modifies a file. Close processing can then use the current DOS clock when no valid explicit time remains.

The practical sequencing rule is therefore straightforward: finish data writes before assigning the intended last-write time. A routine that sets the timestamp, writes another block, and closes can lose the preserved time. This is not a random filesystem failure; it follows from the kernel’s handling of modified file state.

A copy utility can use the following operational sequence:

  1. Open the source and retrieve its last-write words.
  2. Create or open the destination according to a documented overwrite policy.
  3. Copy the data, checking errors and returned write lengths.
  4. Set the destination’s time and date after the final data write.
  5. Close the destination and check the close result.
  6. Reopen the destination, retrieve its values, and compare them.

This list describes one file’s lifecycle, not a transaction across an entire directory tree. If an error interrupts the operation, distinguish a partial destination from a successfully copied file whose metadata assignment failed. Do not delete or replace a valuable existing destination as an implicit cleanup policy.

Set and verify without hiding the close boundary

The assignment fragment uses the same naming convention:

    mov bx, [destination_handle]
    mov cx, [saved_time]
    mov dx, [saved_date]
    mov ax, 5701h
    int 21h
    jc  set_time_failed

    mov bx, [destination_handle]
    mov ah, 3Eh
    int 21h
    jc  close_failed

After close, do not reuse the integer as if it still belonged to that file. DOS can allocate it for another open operation. Reopen the intended destination explicitly, retrieve with 5700h, compare CX and DX with the saved words, then close the verification handle.

Closing is also distinct from a universal physical-durability guarantee. Buffered drivers, caches, removable media, and network servers add their own behavior. If power-loss durability is part of the acceptance requirement, test the supported commit and storage path separately. A metadata readback inside the same session is evidence of logical visibility, not proof against every later hardware failure.

Devices, redirectors, and invalid data need their own policy

In the reviewed FreeDOS assignment code, a System File Table entry marked as a device causes the operation to return success without changing ordinary file metadata. This is why success alone cannot prove that an application targeted a regular file. Establish the handle’s origin and object type before using the result as a preservation claim.

Network redirectors and alternate DOS-compatible implementations can introduce additional semantics. Record the kernel version and whether the file is local FAT storage or a redirected path. A test performed on a local virtual disk should not be reported as validation of an unrelated remote server.

The setter in the reviewed kernel code assigns packed words; callers should not assume that it validates every calendar combination. A program accepting human-entered timestamps should validate dates and times before packing them. A preservation utility copying raw words from a damaged source needs a different policy: retain malformed values as evidence or reject them explicitly, rather than silently normalizing them into an invented date.

Build a bounded acceptance test

Use a disposable regular file and a known valid timestamp. Verify get/set/readback, then deliberately test the difference between setting after the final write and setting before a later write. Compare values after close and reopen, not only immediately after assignment. Also test an invalid closed handle and confirm that the error path records the failure without pretending metadata was preserved.

For a copy workflow, compare data length and an independently computed content hash in addition to timestamp words. Two files can share identical last-write metadata while containing different bytes. Conversely, identical bytes with a changed timestamp mean that content transfer succeeded but the metadata contract did not.

The trustworthy outcome is a recorded lifecycle: owned regular-file handles, checked writes, final timestamp assignment, checked close, and fresh-handle readback. AH=57h is small, but correct use requires knowing when file state changes and what the returned words can actually prove.

Related:

Sources:

Comments