Each directory entry has an attribute byte. INT 21h AH=43h with AL = 0 returns it in CX; with AL = 1 it writes CX into it. The file is found by name, like 41h and 56h, and does not need to be open.
| Bit | Value | Meaning |
|---|---|---|
| 0 | 01h | read-only: DOS refuses to write, truncate or delete the file (05h) |
| 1 | 02h | hidden: DIR does not list it, find first skips it unless asked |
| 2 | 04h | system: like hidden, for DOS's own files |
| 3 | 08h | volume label: the entry is the disk's name, not a file |
| 4 | 10h | directory |
| 5 | 20h | archive: DOS sets it whenever the file is written; backup programs clear it |
Set can change bits 0, 1, 2 and 5. The directory and volume-label bits say what the entry is, and trying to set them is error 05h. Set replaces the whole byte, so to change one bit, get the attributes first, change the bit with OR or AND, and set the result.
MOV AH, 3Ch ; DOS: create TEST.TXTXOR CX, CX ; CX = 0: normal attributesMOV DX, OFFSET FNAMEINT 21hCALL CHECK ; stop if CF = 1MOV BX, AX ; BX = the handleMOV AH, 3Eh ; close itINT 21hMOV AX, 4300h ; DOS: get the attributes of TEST.TXTMOV DX, OFFSET FNAMEINT 21hCALL CHECKMOV DX, OFFSET M1MOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AL, CL ; CX = the attributesCALL PHEXBMOV DX, OFFSET CRLFMOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AX, 4301h ; set: read-only + archiveMOV CX, 21hMOV DX, OFFSET FNAMEINT 21hCALL CHECKMOV AX, 4300h ; get them againMOV DX, OFFSET FNAMEINT 21hCALL CHECKMOV DX, OFFSET M2MOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AL, CLCALL PHEXBMOV DX, OFFSET CRLFMOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AX, 4301h ; back to 20h, or the delete fails with 05hMOV CX, 20hMOV DX, OFFSET FNAMEINT 21hCALL CHECKMOV AH, 41h ; delete itMOV DX, OFFSET FNAMEINT 21hFNAME DB "TEST.TXT", 0M1 DB "new file: $"M2 DB "after setting read-only: $"CRLF DB 13, 10, "$"CHECK PROC NEAR ; CF = 1: print the error code in AL and exitJNC CHECK1PUSH AXMOV DX, OFFSET FAILMSGMOV AH, 09hINT 21hPOP AXCALL PHEXBMOV AX, 4C01h ; exit with code 1INT 21hCHECK1:RETCHECK ENDPFAILMSG DB "failed: error $"PHEXB PROC NEAR ; print AL as two hex digitsPUSH CXPUSH DXMOV DH, AL ; keep the byteMOV CL, 4SHR AL, CL ; the high digit firstMOV CX, 2PHEXB1:AND AL, 0FhADD AL, "0"CMP AL, "9"JBE PHEXB2ADD AL, 7 ; 10..15 -> "A".."F"PHEXB2:MOV DL, ALMOV AH, 02h ; DOS: print DLINT 21hMOV AL, DH ; then the low digitLOOP PHEXB1POP DXPOP CXRETPHEXB ENDP
A new file comes with the archive bit set: it has changed since the last backup, which was never. Get works on directories too, returning 10h plus any other bits, which makes it a quick test whether a name is a directory.
On a host folder DOSBox-X maps read-only onto the host file's write permission and, on hosts other than Windows, keeps hidden and system in a small file of its own. How strictly read-only is enforced then depends on the host: a DOSBox-X running with administrator rights may still be able to write the file.