The early history of Windows file attributes, and why there is a gap between System and Directory


Let's look at the values for the basic Windows file attributes. There's a gap where 8 should be.

FILE_ATTRIBUTE_... Value
READONLY 0x00000001
HIDDEN 0x00000002
SYSTEM 0x00000004
  0x00000008
DIRECTORY 0x00000010
ARCHIVE 0x00000020

Rewind to CP/M.

CP/M supported eleven attributes:

Name Meaning
F1, F2, F3, F4 User-defined
F5, F6, F7, F8 Interface-defined
T1 Read-only
T2 System
T3 Archive

The operating system imposed no semantics for user-defined attributes. You can use them for whatever you want.

The meanings of the interface-defined attributes were defined by each operating system interface. Think of them as four bonus flag parameters for each syscall that takes a file control block. You could set interface-defined attributes before calling a function, and that passed four additional flags in. Or the function could manipulate those attributes before returning, allowing it to return four flags out. Interface-defined attributes are always clear on disk.

The read-only bit marked a file as read-only.

The system bit had two effects: First, it hid the file from directory listings. Second, if the file belonged to user 0,¹ then the file was available to all users. (This was handy for program files.)

The archive bit reported whether the file has been backed up.

These attributes were retrofitted onto the existing directory structure by taking over the high bits of the eleven filename characters! That's why they are named F1 through F8 (high bits of the eight-character file base name) and T1 through T3 (high bits of the three-character extension, also known as the file type).

You can infer from this that CP/M file names were limited to 7-bit ASCII.

Anyway, MS-DOS 1.0 split the dual meaning of the system attribute into two attribute (hidden and system), and even though it didn't implement the read-only attribute, it reserved space for it.

That explains why the first three attributes are read-only (1), hidden (2), and system (4).

MS-DOS 2.0 most notably added support for subdirectories, but another feature that came along was volume labels. Since there was no space for the volume label in the disk header, the volume label was added as a directory entry in the root directory, with a special attribute that says "This is a volume label, not a file."²

The next attributes became volume label (8), directory (16), and archive (32).

Win32 adopted the same file attribute values as MS-DOS and 16-bit Windows, presumably in an effort to minimize surprises when porting code from 16-bit to 32-bit. The volume label attribute disappeared from Win32, but the bits for directory and archive were left at their original values to avoid problems with programs that operated with file attributes. Those programs contained their own definitions for the file attributes because 16-bit Windows didn't provide any.

¹ CP/M supported up to 16 users, numbered 0 through 15. When you started the computer, you were user 0, but you could change users by saying USER n. Files belonging to other users were inaccessible, except that system files belong to user 0 were available to everyone. Anybody could change to any user at any time, so this was a file organization feature, not a security feature. In practice, nobody really used it because floppy discs were so small that it was easier to organize your files by putting them on different floppies than by trying to remember which user you used for each file.

² Windows 95 later repurposed the volume label attribute to mark directory entries as being used for long file names. Disk utilities often parsed directory entries directly, so any change in the disk format was a compatibility risk. The choice to use the volume label attribute for this purpose came after a lot of experimentation to find the least disruptive file format for long file names. It turns out that most low-level disk utility programs ignored anything marked with the volume label attribute.

Comments (24)
  1. Antonio Rodríguez says:

    Back in the day (around 1997 or so), I used DOS 6 and NT 4, with all partitions in FAT16 for ease of use (the computer had two hard drives of 420 MB and 1 GB, so cluster size wasn’t a big issue). One day I tried a third-party DOS defrag utility which offered an option to sort the directories. The next time I booted NT, it refused to log on, because it couldn’t find my user profile. Booting from a DOS 7 floppy, I discovered the utility had trashed every single long filename! That’s when I learned that VFAT stored the long filenames in “invalid” directory entries, which the defrag utility had tried “helpfully” to sort – detaching them from the original file entries. Of course, I had to reinstall NT.

    1. Mike Swanson says:

      Windows 95 included a “LFNBACK” utility on the CD-ROM, to be run from pure DOS mode, that would backup long file names and what short names they are associated with, so that you might be able to use DOS defraggers or backup/restore utilities that did such things. Probably would have worked for Windows NT too (afaik the VFAT structures are identical, though NT and 95 had different short-name generation rules).

  2. John Elliott says:

    Amusingly, when MSDOS introduced the archive bit, it did it with the opposite meaning of how it was done in CP/M. In CP/M the archive bit would be cleared when the file was touched, and the backup program would set it. In MSDOS touching the file sets the archive bit, and the backup program clears it. Doubtless there was a good reason.

    1. morlamweb says:

      It’s in the semantics attached to the Archive flag. The way that I’ve always understood it in Windows is that a file with the Archive flag raised means “this file is ready to be archived”. Backup programs – and I’ve known quite a few – generally use that file to determine which ones should be backed up and lower the flag when the backup is performed. The opposite semantics – archive flag raised = “file has been archived/backed up” also work. I have no idea why CP/M went one way and DOS/Windows went the other.

      1. Jeff says:

        Because there were two ways to do it, and two development teams. Murphy strikes again!

  3. Joshua says:

    To be fair, every Windows program provides its own copy of the file attributes list due to the magic of constant folding at compile time.

    1. Win32 strove for source code compatibility with Win16. Binary compatibility was not the issue. If the constants had names, then they could be renumbered by changing the #define. But these constants didn’t have names in Win16, so no #define to modify.

  4. Brian says:

    There were extended CP/M systems that were multi-user, kinda-sorta. My first employer used have a somewhat complex multi-user client/server system that was CP/M based (though the file system was extended to allow for hard disks). It was *very* fragile.

    1. Ismo says:

      If I remember correctly the multiuser CP/M was called MP/M. Never saw one myself.

      1. Richard Wells says:

        MP/M was the first version of CP/M intended for multiple users. It was followed by Concurrent CP/M which became Concurrent DOS which stripped of multi-user and CP/M functionality became the basis of DR-DOS. Other companies did their own modifications to CP/M to meet the nascent multi-user market making full use of the USER information. Some early hard drive setups also used USER. When the file system only permits 8 MBs and the hard drives are larger, something has to give.

  5. DWalker07 says:

    Since you said that MS-Dos 1.0 provided a bit for the read-only attribute, but did not implement it, when did the read-only attribute get implemented?

    Since Windows 95 repurposed the “08” bit to mark long file names, isn’t that bit still used for that purpose? I don’t see the “08” bit in the linked article.

    1. Erik F says:

      It’s an implementation detail of the FAT LFN mechanism, so it makes sense that it’s not exposed to user programs. NTFS and UDF don’t store LFNs that way, and volume labels are managed using SetVolumeLabel.

    2. Antonio Rodríguez says:

      It may be an implementation detail, but it is widely known. It is documented in Wikipedia, and IIRC even Raymond has talked about it in this blog. The “magical” combination is label, system, hidden and read only flags (attribute byte 0x0f), which is something illogical that you won’t see in the wild. The volume flag only has a meaning in the root directory, and most (all?) implementations of FAT scan sequentially for it, so Microsoft’s VFAT implementations go as far as creating a blank volume label in the root directory if none exists before writing the first long filename.

      1. Joshua says:

        Filesystem on-disk formats aren’t implementation details.

    3. Neil says:

      I’ve recently noticed that the Hidden attribute is also documented to make a file read-only. I found out the hard way when troubleshooting an application which was unable to update one of its data files because something had marked it hidden (though I have no idea what, why or how).

      1. Then there’s also this quirk with CreateFile when opening hidden or system files (https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea):

        > If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.

        As a result, any software that tries to open a hidden or system file like fopen(filename, “w”) will get ERROR_ACCESS_DENIED, even if the user *does* have permission to write to the file.

    4. Richard says:

      The 08 (attribute) bit is still used on FAT12, FAT16, and FAT32 File Systems to denote “Volume Label”. I just used the LABEL command on a FAT32 File System with Windows 10 v1803. A low-level disk editor indeed shows the 08 attribute bit set for the Volume Label in the root directory.

      My guess as to why the flag was removed from the Win32 API: the 08 flag has no meaning on NTFS (and later exFAT and ReFS).

      1. It doesn’t even have meaning on FAT. You can’t CreateFile a volume label.

        1. Richard says:

          You also can’t CreateFile a directory, but there’s an attribute bit for a directory. I always assumed the flags are there for the on-disk representation.

          1. You can too CreateFile directory (use backup semantics). You also can see them in FindFirstFile. But volume labels don’t show up in FindFirstFile.

          2. Eryk Sun says:

            > You also can’t CreateFile a directory

            It’s undocumented, but you can create a new directory via CreateFile (as opposed to CreateDirectory) by combining CREATE_NEW disposition with the attributes/flags FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_BACKUP_SEMANTICS. In the NT API we just need the create option FILE_DIRECTORY_FILE.

        2. Yuhong Bao says:

          It was used in DOS when using the FCB calls to create a volume label.

          1. Yes, but we’re talking about Windows here.

Comments are closed.

Skip to main content