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 CreateFile
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
CreateFile
), 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 STANDARD_RIGHTS_READ (READ_CONTROL) #define STANDARD_RIGHTS_WRITE (READ_CONTROL) #define STANDARD_RIGHTS_EXECUTE (READ_CONTROL) #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 #define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) #define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\ FILE_READ_DATA |\ FILE_READ_ATTRIBUTES |\ FILE_READ_EA |\ SYNCHRONIZE) #define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\ FILE_WRITE_DATA |\ FILE_WRITE_ATTRIBUTES |\ FILE_WRITE_EA |\ FILE_APPEND_DATA |\ SYNCHRONIZE) #define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\ FILE_READ_ATTRIBUTES |\ FILE_EXECUTE |\ SYNCHRONIZE)
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
.