C# .NET - how to determine if directory is writabl

2020-07-09 10:05发布

I'm working on a piece of software that needs to copy a file to a given directory on the filesystem. It needs to work on both UAC-aware OSs (Vista, 7) as well as XP. To get around the issue of writing to a directory where UAC elevation is required, the app actually kicks off another process with a manifest that states that UAC is required. This generates the prompt and then does the copy when the user confirms.

From what I can see, a directory can have three different logical permission states - writeable without UAC elevation, writeable with UAC elevation and not writeable.

My question is this: For a given directory, how do I reliably determine whether the current user can copy (and potentially overwrite) a file to that directory, and if I can, how do I determine if UAC elevation is required?

On XP, this could just be as simple as checking whether the 'Allow Write' permission is granted, but on Vista / 7, there are directories where this permission isn't granted, but this action is still possible with UAC.

2条回答
三岁会撩人
2楼-- · 2020-07-09 10:37

You handle the writable without elevation case just by trying the operation. It's when that fails, and you have to distinguish between not-writable vs writable via UAC elevation that is potentially difficult.

I don't think that I would like programs trying to figure that out for me (since they'll inevitably get it wrong quite often).

I think it's safe to design it with these assumptions:

  • Administrators sometimes run as restricted accounts to trial software they don't trust -> if your app is going to make invasive changes to the computer that require UAC they want to cancel, not elevate.
  • Elevated administrators can write the file (they are admin after all) -> no need for an actual ACL check, detecting a restricted token is enough.
  • Users may elevate using a different account, or may ask a co-worker to complete the UAC-required action -> checking for the restricted token will miss these cases.
  • Other recoverable things cause access denied, including file in use -> sometimes the right thing to do is retry using the same restricted permissions.

So altogether, I would suggest trying the operation AsInvoker, in case of access denied bring up a prompt that explains that Windows denied the operation, possible causes are: file-in-use, elevation required, administrator credentials required, and give the user three buttons:

  • Cancel
  • Retry with current credentials
  • (shield icon) Elevate permissions and retry
查看更多
Summer. ? 凉城
3楼-- · 2020-07-09 10:49

We have a method for WriteAccess on files, you can probably adapt it for Directories (Directory.GetAccessControl and so on)

    /// <summary> Checks for write access for the given file.
    /// </summary>
    /// <param name="fileName">The filename.</param>
    /// <returns>true, if write access is allowed, otherwise false</returns>
    public static bool WriteAccess(string fileName)
    {
        if ((File.GetAttributes(fileName) & FileAttributes.ReadOnly) != 0)
            return false;

        // Get the access rules of the specified files (user groups and user names that have access to the file)
        var rules = File.GetAccessControl(fileName).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));

        // Get the identity of the current user and the groups that the user is in.
        var groups = WindowsIdentity.GetCurrent().Groups;
        string sidCurrentUser = WindowsIdentity.GetCurrent().User.Value;

        // Check if writing to the file is explicitly denied for this user or a group the user is in.
        if (rules.OfType<FileSystemAccessRule>().Any(r => (groups.Contains(r.IdentityReference) || r.IdentityReference.Value == sidCurrentUser) && r.AccessControlType == AccessControlType.Deny && (r.FileSystemRights & FileSystemRights.WriteData) == FileSystemRights.WriteData))
            return false;

        // Check if writing is allowed
        return rules.OfType<FileSystemAccessRule>().Any(r => (groups.Contains(r.IdentityReference) || r.IdentityReference.Value == sidCurrentUser) && r.AccessControlType == AccessControlType.Allow && (r.FileSystemRights & FileSystemRights.WriteData) == FileSystemRights.WriteData);
    }

Hope this helps.

查看更多
登录 后发表回答