Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

DOS INT 21h File Creation: Temporary Names and No-Clobber Semantics

Choose between DOS create, exclusive-create, and temporary-file calls; understand the returned handle, pathname buffer, failure modes, and cleanup lifecycle.

DOS exposes three handle-based operations that look similar but encode different file-creation intent. INT 21h function 3Ch creates a named file and, when that name already exists, truncates the existing file. Function 5Bh creates a new named file only when the name is not already present. Function 5Ah asks DOS to generate a temporary name beneath a caller-supplied directory and returns both a handle and the resulting path. Choosing the wrong function can overwrite a user’s file, turn a retry into data loss, or leave temporary work files behind after a program exits.

The historical MS-DOS programming reference documents these functions and their register contracts. Treat its version statements as DOS-family compatibility history, not a promise that every FreeDOS kernel, redirector, or filesystem handles every edge case identically. Test the target kernel and storage path before using the calls for persistent application data.

The destructive behavior of function 3Ch

Function 3Ch receives a null-terminated pathname through DS:DX and a file attribute in CX. It returns a handle in AX when successful. The important semantic is that creating an already-existing pathname is not a no-op: the existing file is opened with its contents truncated. A “create or open” retry loop that blindly calls 3Ch can therefore destroy prior output when a previous attempt already produced the destination.

Use 3Ch when replacement is intentional and the application has already established the correct target. If the workflow must preserve an existing file, do not probe with a separate OPEN followed by CREATE and assume that the two-call sequence is indivisible. Another process can create the name between the check and the create. Use an exclusive-create operation where supported and handle its collision result.

Function 5Bh is the no-existing-name operation

Function 5Bh is documented for DOS 3.0 and later in the MS-DOS Encyclopedia as Create New File. It accepts a path and attributes like the ordinary create call, but fails if a file with that name already exists. That is useful when an application needs to reserve a distinct name or must never truncate an existing destination. The caller must check the carry flag and AX error code; a failed call is not proof that the target is absent, because other conditions such as a missing directory, exhausted handles, or access restrictions can also fail.

Do not treat 5Bh as a universal multi-process database lock. Historical references discuss its use in network synchronization patterns, but the real behavior depends on the redirector, server, filesystem, and cache path. A successful create can reserve a pathname only within the semantics supplied by that stack; it does not add crash recovery, lease expiry, durable transactions, or automatic cleanup. An application using a sentinel file must define how stale sentinels are diagnosed and removed after an abnormal shutdown.

Function 5Ah creates a name beneath a supplied path

Function 5Ah is designed for temporary work files. The caller supplies a null-terminated directory path ending in a backslash, followed by writable space in the same buffer. The historical DOS contract reserves 13 bytes after the path for the generated name and terminator. DOS appends a generated component and returns the complete pathname in the buffer plus an open file handle in AX.

; MASM-style illustrative data and call sequence.
; The path must identify an existing directory and end in backslash.
temp_path db 'C:\TEMP\',0,13 dup (?)
temp_handle dw ?

        mov dx, seg temp_path
        mov ds, dx
        mov dx, offset temp_path
        xor cx, cx               ; normal file attributes
        mov ah, 5Ah              ; create temporary file
        int 21h
        jc  temp_create_failed
        mov temp_handle, ax
        ; temp_path now contains the generated ASCIIZ pathname.

The sequence is an interface illustration, not a complete program: a real routine must preserve registers according to its calling convention, ensure DS is valid, inspect the returned error, and keep the buffer alive until it has copied the generated pathname. The directory must exist and be writable. Do not omit the spare bytes or place unrelated state immediately after the pathname buffer.

The generated component is described in historical DOS documentation as derived from system time and unique for the function’s intended collision-avoidance purpose. That does not make it cryptographically unpredictable or suitable as a secret. Do not use it as a credential, nonce, or security boundary. The program should still handle creation failure and avoid assuming a particular filename pattern across kernels.

A handle and a pathname have separate lifetimes

The handle is the live DOS reference used for READ, WRITE, SEEK, and CLOSE. The path is the name used later for deletion or rename. Capture both: closing the handle does not remove the directory entry, and terminating a process does not automatically delete a temporary file. If the program crashes after creating the file, the disk can retain an orphan that must be identified by the application’s own naming policy or a controlled maintenance process.

A robust temporary-file workflow has explicit states:

  1. Validate the destination directory and available storage.
  2. Create a temporary file and retain both handle and returned path.
  3. Write the complete output, checking every short write and DOS error.
  4. Close the handle and check the close result before publishing the result.
  5. Rename or otherwise publish only after validation succeeds.
  6. Delete the temporary path on every failure path where the file is no longer needed.

The first write can be short even when the call did not report a fatal error, so a producer should compare the returned byte count with the requested count and continue only with the unwritten suffix. A zero-byte write while bytes remain is a failure condition for a forward-progress loop. Preserve the first write error before attempting close or delete, because cleanup calls can replace the most recent DOS error state. If close itself fails, do not mark the output as published merely because every prior write returned.

Name allocation and file-content durability are separate concerns. 5Ah chooses a name and creates an open file; it does not promise that later writes survive power loss, that a rename is atomic on every DOS-compatible filesystem, or that directory metadata is synchronized as one transaction. If the application is generating an important artifact, keep a previous known-good copy until the replacement has been verified and use the target kernel’s documented commit/close behavior. Avoid cleanup rules based only on a filename prefix; an active program or unrelated application may be using a matching file.

This sequence is not a claim of filesystem atomicity or power-loss durability. DOS and FAT operations do not automatically provide modern transactional guarantees. The INT 21h commit call, when supported, is a separate request to flush file state; it does not turn a multi-step create/write/rename sequence into an atomic transaction. Keep backups and design recovery around the storage medium’s actual behavior.

Diagnose errors at the point of failure

For these handle-based calls, test the carry flag immediately after INT 21h; when set, AX contains a DOS error code. Function 5Ah can fail because the directory is invalid or unavailable, no handle is available, or access is denied. Function 5Bh can fail because a name already exists, in addition to ordinary path and capacity errors. Do not print “file exists” for every failed call. If the application needs more detail and the environment supports it, read extended error information with function 59h before making another DOS call that may replace the last error state.

Test in a disposable directory using a pre-existing destination, a missing directory, a full or read-only volume, and a simulated write failure. Confirm that the caller neither truncates the pre-existing file nor mistakes a partial write for success. Then force an abnormal termination and verify that the maintenance procedure can identify leftovers without deleting unrelated user data.

The practical rule is simple: use 3Ch only when replacement is intended, 5Bh when an existing name must cause failure, and 5Ah for a generated work filename whose cleanup the application owns. Verify the DOS-family implementation, redirector, and filesystem as one system. A successful function return establishes that DOS created a file and returned a handle; it does not establish that the application wrote complete data, closed it successfully, published it safely, or removed it later.

Related:

Sources:

Comments