Nasty gotcha: STGM_READ | STGM_WRITE does not grant read/write access


You might think that if you want to get read/write access, you could pass STGM_READ | STGM_WRITE. You would be wrong. You have to pass STGM_READ­WRITE.

The three flags STGM_READ, STGM_WRITE, and STGM_READ­WRITE are mutually exclusive. If you try to combine them, you get a weird mess.

In particular, since the numerical value of STGM_READ is zero, passing STGM_READ | STGM_WRITE is numerically equivalent to passing STGM_WRITE, which grants write-only access.

The documentation for the STGM_* constants specifically says “It is not valid to use more than one element from a single group,” and STGM_READ and STGM_WRITE belong to the Access group (as does STGM_READ­WRITE).

These values date back to the days of MS-DOS, where function 3Dh (Open File) passed an access mode in the AL register.

7 6 5 4 3 2 1 0
0 0 0 0 0 access
mode

The bottom three bits specified the requested access (0 = read-only, 1 = write-only, 2 = read/write), and the remaining bits were reserved.

Later, when networking support was added in approximately MS-DOS 3.5, three more bits were pressed into service:

7 6 5 4 3 2 1 0
0 share
mode
0 access
mode

Sharing modes were 0 = compatibility mode, 1 = deny all, 2 = deny write, 3 = deny read, 4 = deny none.

These values were carried forward into Windows as flags to the Open­File function:

Value Description
OF_READ
0x00000000
Opens a file for reading only.
OF_WRITE
0x00000001
Opens a file for write access only.
OF_READ­WRITE
0x00000002
Opens a file with read/write permissions.
OF_SHARE­COMPAT
0x00000000
Opens a file with compatibility mode, allows any process on a specified computer to open the file any number of times.
OF_SHARE­EXCLUSIVE
0x00000010
Opens a file with exclusive mode and denies both read/write ccess to other processes.
OF_SHARE­DENY­WRITE
0x00000020
Opens a file and denies write access to other processes.
OF_SHARE­DENY­READ
0x00000030
Opens a file and denies read access to other processes.
OF_SHARE­DENY­NONE
0x00000040
Opens a file without denying read or write access to other processes.

These flags were then carried forward into the STGM constants with the same numerical values.

Comments (10)
  1. laonianren says:

    This seemed like a strange design choice until I remembered that OLE2 predates Windows NT and 95.

    P.S. Congratulations on the completion of your first decade.  The blog is an impressive piece of work.

  2. Random832 says:

    This isn't the only function like this – open() is the same way, both on MSVC and on many Unix systems (including the original). I wonder if that's where DOS got it from.

    I'm somewhat confused that you say the bottom three bits are used, when the values only occupy two bits.

    [Values 3 through 7 were reserved for future use. -Raymond]
  3. Joshua says:

    And here begins the problem of so many programs opening files with delete locks when they don't need it or have any reason to care.

    @Random832: And on UNIX mode 3 means open handle to symbolic link (only on some kernels). The handle has a few uses, but read and write aren't among them.

  4. Gabe says:

    If only they had thought to make the names "STGM_READ_ONLY" and "STGM_WRITE_ONLY", then there might not be a problem. Few people would attempt to combine read-only with write-only.

  5. Dave says:

    Every time I see those STGM_* constants I pronounce them "stigmata". Which is actually pretty appropriate considering the pain they have put me through in the past.

  6. Simon Farnsworth says:

    @Random832 It goes back long before UNIX; AFAICT, tape-only OSes used a single bit for "read or write?"; when disks came along, to keep compatibility, they expanded to a 2 bit field, and made the third option "both read and write". That historical accident appears to have been pulled forward ever since.

  7. Faxmachinen says:

    @Simon: Interesting piece of knowledge. There's nothing stopping you from having both 0x0 and 0x2 mean "read" though (assuming 0x1 used to mean write), so I don't know what they were thinking.

  8. yuhong2 says:

    MS-DOS 3.5?

  9. Joseph Koss says:

    This "problem" is caused by the values of constants being so easily buried in include files or the other similar methods (such as type libraries.) The values themselves indicate that they are not rooted in bit positions.

  10. Cheong says:

    @Yuhong Bao: "approximately", so it is either on 3.x range or 4.x.

Comments are closed.