Raymond Chen

A customer wanted to know whether passing GENERIC_ALL as an access mask is effectively equivalent to passing GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE. Specifically, they were interested in the answer to this question with respect to the Create­File function.

Okay, first question first. Is GENERIC_ALL effectively equivalent to GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE?

The answer is “It depends.”

Each object decides what these generic access masks mean. Now, the intended use is that GENERIC_READ correspond to whatever “read” access means for an object, GENERIC_WRITE correspond to whatever “write” access means for an object, and GENERIC_EXECUTE correspond to whatever “execute” access means for an object. It’s also the intended use that GENERIC_ALL represent whatever access makes the most sense for “all access”.

But that’s just the intended use. There is nothing physically preventing an object from giving those four generic access masks nonsensical values. Because anybody can make up a generic mapping. Therefore, there’s nothing you can guarantee about the relationship between the generic access masks beyond “they are what the object decides they are.”

In practice, GENERIC_ALL is at least as big as GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, but it can be bigger. For example, for files (which is probably what the customer is asking about when they talk about Create­File), the values are defined as follows, in winnt.h:

#define DELETE                           (0x00010000L)
#define READ_CONTROL                     (0x00020000L)
#define WRITE_DAC                        (0x00040000L)
#define WRITE_OWNER                      (0x00080000L)
#define SYNCHRONIZE                      (0x00100000L)

#define STANDARD_RIGHTS_REQUIRED         (0x000F0000L)


#define FILE_READ_DATA            ( 0x0001 )    // file & pipe
#define FILE_LIST_DIRECTORY       ( 0x0001 )    // directory

#define FILE_WRITE_DATA           ( 0x0002 )    // file & pipe
#define FILE_ADD_FILE             ( 0x0002 )    // directory

#define FILE_APPEND_DATA          ( 0x0004 )    // file
#define FILE_ADD_SUBDIRECTORY     ( 0x0004 )    // directory
#define FILE_CREATE_PIPE_INSTANCE ( 0x0004 )    // named pipe

#define FILE_READ_EA              ( 0x0008 )    // file & directory

#define FILE_WRITE_EA             ( 0x0010 )    // file & directory

#define FILE_EXECUTE              ( 0x0020 )    // file
#define FILE_TRAVERSE             ( 0x0020 )    // directory

#define FILE_DELETE_CHILD         ( 0x0040 )    // directory

#define FILE_READ_ATTRIBUTES      ( 0x0080 )    // all

#define FILE_WRITE_ATTRIBUTES     ( 0x0100 )    // all


                                   FILE_READ_DATA           |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_READ_EA             |\

                                   FILE_WRITE_DATA          |\
                                   FILE_WRITE_ATTRIBUTES    |\
                                   FILE_WRITE_EA            |\
                                   FILE_APPEND_DATA         |\

                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_EXECUTE             |\

Right off the bat, you can see that of the standard rights, FILE_ALL_ACCESS includes STANDARD_RIGHTS_REQUIRED, whereas the FILE_GENERIC_* values include only STANDARD_RIGHTS_*, all of which are defined as READ_CONTROL. This means that FILE_ALL_ACCESS includes DELETE, WRITE_DAC, and WRITE_OWNER which are not included in any of the other generic access masks. (SYNCHRONIZE is explicitly added by all of the FILE_GENERIC_* access masks.)

If you study it a bit more, you’ll see that FILE_ALL_ACCESS also includes FILE_DELETE_CHILD, which is not present in any of the other generic access masks.

So even in the specific case of file access, we see that GENERIC_ALL is not equivalent to GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE.