Permissions Not Included In .NET AccessRule.FileSystemRights Enum

June 28, 2011 — 12 Comments

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 (I do wish they wouldn’t name some properties with the same name as a Type but hey).

The end result of this is that for some files/directories you simply cannot determine which permissions are assigned to them. 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:

 

Public Enum GenericRights As Integer
    GENERIC_READ = &H80000000
    GENERIC_WRITE = &H40000000
    GENERIC_EXECUTE = &H20000000
    GENERIC_ALL = &H10000000
End Enum

Public Enum MappedGenericRights As Integer
    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:

 

Public Shared Function MapGenericRightsToFileSystemRights(ByVal OriginalRights As FileSystemRights) As FileSystemRights
      Dim MappedRights As FileSystemRights = Nothing
      If CBool(OriginalRights And GenericRights.GENERIC_EXECUTE) Then
          MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_EXECUTE, FileSystemRights)
      End If
      If CBool(OriginalRights And GenericRights.GENERIC_READ) Then
          MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_READ, FileSystemRights)
      End If
      If CBool(OriginalRights And GenericRights.GENERIC_WRITE) Then
          MappedRights = CType(MappedRights Or MappedGenericRights.FILE_GENERIC_WRITE, FileSystemRights)
      End If
      If CBool(OriginalRights And 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:

 

'Get the permissions for this file/directory
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

12 responses to Permissions Not Included In .NET AccessRule.FileSystemRights Enum

  1. 

    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.

  2. 

    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.

    • 
      Juan C. Becerra February 16, 2012 at 02:15

      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…

      • 
        Juan C. Becerra February 16, 2012 at 19:37

        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.

  3. 
    Andreas Elbers March 29, 2012 at 13:47

    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

  4. 
    Andreas Elbers March 29, 2012 at 15:28

    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;
    }

    • 
      Juan C. Becerra March 31, 2012 at 00:45

      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;
      }

  5. 
    Chris Griffiths August 8, 2012 at 09:46

    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 = 0×80000000;
    GENERIC_WRITE = 0×40000000;
    GENERIC_EXECUTE = 0×20000000;
    GENERIC_ALL = 0×10000000;
    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
    }

  6. 

    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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s