Using .NET you may think that determining which permissions are assigned to a directory/file should be quite easy, as there is a FileSystemRights Enum defined that seems to contain every possible permission that a file/directory can have and calling AccessRule.FileSystemRights returns a combination of these values. However, you will soon come across some permissions where the value in this property does not match any of the values in the FileSystemRights Enum.
The end result of this is that for some files/directories you cannot determine which permissions are assigned to them using this FileSystemRights enum alone. If you do AccessRule.FileSystemRights.ToString then for these values all you see is a number rather than a description (e.g Modify, Delete, FullControl etc). Common numbers you might see are:
-1610612736, –536805376, and 268435456
To work out what these permissions actually are, you need to look at which bits are set when you treat that number as 32 separate bits rather than as an Integer (as Integers are 32 bits long), and compare them to this diagram: http://msdn.microsoft.com/en-us/library/aa374896(v=vs.85).aspx
So for example, -1610612736 has the first bit and the third bit set, which means it is GENERIC_READ combined with GENERIC_EXECUTE. So now you can convert these generic permissions into the specific file system permissions that they correspond to.
You can see which permissions each generic permission maps to here: http://msdn.microsoft.com/en-us/library/aa364399.aspx. Just be aware that STANDARD_RIGHTS_READ, STANDARD_RIGHTS_EXECUTE and STANDARD_RIGHTS_WRITE are all the same thing (no idea why, seems strange to me) and actually all equal the FileSystemRights.ReadPermissions value.
So for example, GENERIC_READ (aka FILE_GENERIC_READ) maps to the following file system permissions: ReadAttributes + ReadData + ReadExtendedAttributes + ReadPermissions + Synchronize.
So I made the following two Enums to help keep track of this and help with converting these generic permissions to specific file permissions:
GENERIC_READ = &H80000000
GENERIC_WRITE = &H40000000
GENERIC_EXECUTE = &H20000000
GENERIC_ALL = &H10000000
End Enum
FILE_GENERIC_EXECUTE = FileSystemRights.ExecuteFile Or FileSystemRights.ReadPermissions Or FileSystemRights.ReadAttributes Or FileSystemRights.Synchronize
FILE_GENERIC_READ = FileSystemRights.ReadAttributes Or FileSystemRights.ReadData Or FileSystemRights.ReadExtendedAttributes Or FileSystemRights.ReadPermissions Or FileSystemRights.Synchronize
FILE_GENERIC_WRITE = FileSystemRights.AppendData Or FileSystemRights.WriteAttributes Or FileSystemRights.WriteData Or FileSystemRights.WriteExtendedAttributes Or FileSystemRights.ReadPermissions Or FileSystemRights.Synchronize
FILE_GENERIC_ALL = FileSystemRights.FullControl
End Enum
If you are not familiar with the bitwise operators in .NET then the second enum may seem a bit weird to you. As the FileSystemRights enum has the <Flags> attribute it means we can use the “OR” operator to combine multiple values together. So confusingly the OR operator here kind of means AND (or +) really, but bitwise operations are a whole other topic that there are plenty of articles on the web on. If you are not familiar with them though then I would recommend reading up on it as understanding this comes in handy quite often, like it has done here with this permissions problem.
So now if we wanted to see what our “unrecognised” permissions actually are, we can use a function like this:
Dim MappedRights As FileSystemRights = Nothing
If (OriginalRights And GenericRights.GENERIC_EXECUTE) = GenericRights.GENERIC_EXECUTE Then
MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_EXECUTE, FileSystemRights)
End If
If (OriginalRights And GenericRights.GENERIC_READ) = GenericRights.GENERIC_READ Then
MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_READ, FileSystemRights)
End If
If (OriginalRights And GenericRights.GENERIC_WRITE) = GenericRights.GENERIC_WRITE Then
MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_WRITE, FileSystemRights)
End If
If (OriginalRights And GenericRights.GENERIC_ALL) = GenericRights.GENERIC_ALL Then
MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_ALL, FileSystemRights)
End If
Return MappedRights
End Function
This function takes the original FileSystemRights value (for example –1610612736) and checks to see if any of the generic bits are set. If they are then it sets the relevant bits for the specific permissions that this generic permission maps to and returns a new FileSystemRights value that just contains these specific permissions. So we can then use this function like so:
Dim FullPermissions As FileSystemRights = AccessRule.FileSystemRights
‘Map the generic permissions to specific permissions
FullPermissions = FullPermissions Or MapGenericRightsToFileSystemRights(FullPermissions)
‘Now get rid of the generic permissions so that our value is recognised as a FileSystemRights Enum value
FullPermissions = CType(FullPermissions << 8, FileSystemRights)
FullPermissions = CType(FullPermissions >> 8, FileSystemRights)
Now instead of seeing a value of –1610612736 we see the permissions it actually maps to, which in this example are: ReadAndExecute + Synchronize
Hope it helps others out, as it took me a while to figure out! 🙂 I needed to get this working for my NTFS Permissions Reporter app, which you can find more information on here if you are interested.
Chris
I thought I was loosing my mind… wasted half a day trying to figure this one out. What an oversight from the .NET team! Thanks so much for posting this.
Yeah it does seem pretty daft that they didn’t handle this better, glad my post helped you out anyway 🙂
Great post! I had to translate it to C# to have it work in my project. I can send you the code if you’d like it for reference/use.
Mark, is there any way you can share the code in C#. That would help a lot. Thanks.
Can’t you just run it through a C# translator? Its extremely easy to translate VB.NET to C#.NET and vice versa (hence the fact that several free automated converters exist) so for just these few lines I think it would be pretty easy to do yourself anyway…
I hadn’t thought about the translator, I first tried doing it myself and was getting syntax errors when doing the bitwise operations. After a little I just realized I was misplacing some casts. Thanks a lot.
The translators I used give me errors too can someone give me the C# Code or a link to a translator? I tried to translate it by myself but I get errors
I have it ^^
public static FileSystemRights MapGenericRightsToFileSystemRights(FileSystemRights OriginalRights)
{
FileSystemRights MappedRights = new FileSystemRights();
Boolean blnWasNumber = false;
if (Convert.ToBoolean(Convert.ToInt64(OriginalRights) & Convert.ToInt64(GenericRights.GENERIC_EXECUTE)))
{
MappedRights = MappedRights | FileSystemRights.ExecuteFile | FileSystemRights.ReadPermissions | FileSystemRights.ReadAttributes | FileSystemRights.Synchronize;
blnWasNumber = true;
}
if (Convert.ToBoolean(Convert.ToInt64(OriginalRights) & Convert.ToInt64(TAuthorizationManager.GenericRights.GENERIC_READ)))
{
MappedRights = MappedRights | FileSystemRights.ReadAttributes | FileSystemRights.ReadData | FileSystemRights.ReadExtendedAttributes | FileSystemRights.ReadPermissions | FileSystemRights.Synchronize;
blnWasNumber = true;
}
if (Convert.ToBoolean(Convert.ToInt64(OriginalRights) & Convert.ToInt64(TAuthorizationManager.GenericRights.GENERIC_WRITE)))
{
MappedRights = MappedRights | FileSystemRights.AppendData | FileSystemRights.WriteAttributes | FileSystemRights.WriteData | FileSystemRights.WriteExtendedAttributes | FileSystemRights.ReadPermissions | FileSystemRights.Synchronize;
blnWasNumber = true;
}
if (Convert.ToBoolean(Convert.ToInt64(OriginalRights) & Convert.ToInt64(TAuthorizationManager.GenericRights.GENERIC_ALL)))
{
MappedRights = MappedRights | FileSystemRights.FullControl;
blnWasNumber = true;
}
if (blnWasNumber == false)
{
MappedRights = OriginalRights;
}
return MappedRights;
}
This is what worked for me:
public static FileSystemRights MapGenericRightsToFileSystemRights(FileSystemRights OriginalRights)
{
FileSystemRights MappedRights = 0;
if (Convert.ToBoolean(OriginalRights & (FileSystemRights)GenericRights.GENERIC_EXECUTE))
{
MappedRights = MappedRights | (FileSystemRights)MappedGenericRights.FILE_GENERIC_EXECUTE;
}
if (Convert.ToBoolean(OriginalRights & (FileSystemRights)GenericRights.GENERIC_READ))
{
MappedRights = MappedRights | (FileSystemRights)MappedGenericRights.FILE_GENERIC_READ;
}
if (Convert.ToBoolean(OriginalRights & (FileSystemRights)GenericRights.GENERIC_WRITE))
{
MappedRights = MappedRights | (FileSystemRights)MappedGenericRights.FILE_GENERIC_WRITE;
}
if (Convert.ToBoolean(OriginalRights & (FileSystemRights)GenericRights.GENERIC_ALL))
{
MappedRights = MappedRights | (FileSystemRights)MappedGenericRights.FILE_GENERIC_ALL;
}
return MappedRights;
}
Thanks for posting this info. I needed this functionality in PowerShell so I’ve implemented the following. It is a copy of your function however I also combined the OriginalRights into the final result (after filtering the generic rights out).
$FileSystemRights=[System.Security.AccessControl.FileSystemRights]
$GenericRights=@{
GENERIC_READ = 0x80000000;
GENERIC_WRITE = 0x40000000;
GENERIC_EXECUTE = 0x20000000;
GENERIC_ALL = 0x10000000;
FILTER_GENERIC = 0x0FFFFFFF;
}
$MappedGenericRights=@{
FILE_GENERIC_EXECUTE = $FileSystemRights::ExecuteFile -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::ReadAttributes -bor $FileSystemRights::Synchronize
FILE_GENERIC_READ = $FileSystemRights::ReadAttributes -bor $FileSystemRights::ReadData -bor $FileSystemRights::ReadExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize
FILE_GENERIC_WRITE = $FileSystemRights::AppendData -bor $FileSystemRights::WriteAttributes -bor $FileSystemRights::WriteData -bor $FileSystemRights::WriteExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize
FILE_GENERIC_ALL = $FileSystemRights::FullControl
}
function Map-GenericRightsToFileSystemRights {
param( [System.Security.AccessControl.FileSystemRights]$OriginalRights )
$MappedRights=New-Object -TypeName $FileSystemRights
if ($OriginalRights -band $GenericRights.GENERIC_EXECUTE) {
$MappedRights=$MappedRights -bor $MappedGenericRights.FILE_GENERIC_EXECUTE
}
if ($OriginalRights -band $GenericRights.GENERIC_READ) {
$MappedRights=$MappedRights -bor $MappedGenericRights.FILE_GENERIC_READ
}
if ($OriginalRights -band $GenericRights.GENERIC_WRITE) {
$MappedRights=$MappedRights -bor $MappedGenericRights.FILE_GENERIC_WRITE
}
if ($OriginalRights -band $GenericRights.GENERIC_ALL) {
$MappedRights=$MappedRights -bor $MappedGenericRights.FILE_GENERIC_ALL
}
(($OriginalRights -bAND $GenericRights.FILTER_GENERIC) -bOR $MappedRights) -as $FileSystemRights
}
Thanks for the info. As far as I can determine, as of July 2013, MS has not made any changes to address this issue. My speculation is there are technical reasons or internal politics that block any further improvements.
Yeah I doubt they’ll ever bother changing it to be honest. I can’t see any technical reasons why they wouldn’t be able to do it though – there certainly don’t seem to be any problems with doing it the way I’ve done it as thousands of people are using my NTFS Permissions Reporter program that uses this method to determine the correct permissions and no one has ever reported incorrect permissions being displayed