c# - 当拒绝访问文件与拒绝访问目录时,File.Exists 的行为不同

标签 c# file-io impersonation file-exists

基于MSDN documentation对于 File.ExistsFile.Exists 方法应在出现任何错误时返回 false,包括调用者无权读取文件。

我希望它在文件设置为 FullControl 拒绝用户访问和 FullControl 拒绝用户访问时返回 false文件所在的目录。

我看到的是当用户可以访问目录但不能访问文件时,File.Exists 返回 true;但是,如果用户无权访问该目录,File.Exists 将返回 false

我写了一个小程序来演示我在说什么:

using System;
using System.DirectoryServices;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;

namespace ConsoleApplication1
{
    internal class Program
    {
        private const string DirName = "TestDir";
        private const string FileName = "File.txt";
        private const string Password = "Password1";
        private const string UserName = "PermissionTestUser";
        private static WindowsImpersonationContext Identity = null;
        private static IntPtr LogonToken = IntPtr.Zero;

        public enum LogonProvider
        {
            LOGON32_PROVIDER_DEFAULT = 0,
            LOGON32_PROVIDER_WINNT35 = 1,
            LOGON32_PROVIDER_WINNT40 = 2,
            LOGON32_PROVIDER_WINNT50 = 3
        };

        public enum LogonType
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK = 3,
            LOGON32_LOGON_BATCH = 4,
            LOGON32_LOGON_SERVICE = 5,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
            LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
        };

        public static void Main(string[] args)
        {
            string filePath = Path.Combine(DirName, FileName);
            try
            {
                CreateUser();
                CreateDir();
                CreateFile(filePath);

                // grant user full control to the dir
                SetAccess(DirName, AccessControlType.Allow);
                // deny user full control to the file
                SetAccess(filePath, AccessControlType.Deny);

                // impersonate user
                Impersonate();
                Console.WriteLine("File.Exists (with dir permissions): {0}", File.Exists(filePath));
                UndoImpersonate();

                // deny access to dir
                SetAccess(DirName, AccessControlType.Deny);

                // impersonate user
                Impersonate();
                Console.WriteLine("File.Exists (without dir permissions): {0}", File.Exists(filePath));
                UndoImpersonate();
            }
            finally
            {
                UndoImpersonate();
                DeleteDir();
                DeleteUser();
            }
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);

        private static void CreateDir()
        {
            Directory.CreateDirectory(DirName);
        }

        private static void CreateFile(string path)
        {
            File.Create(path).Dispose();
        }

        private static void CreateUser()
        {
            DirectoryEntry ad = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
            DirectoryEntry newUser = ad.Children.Add(UserName, "user");
            newUser.Invoke("SetPassword", new object[] { Password });
            newUser.Invoke("Put", new object[] { "Description", "Test user" });
            newUser.CommitChanges();
        }

        private static void DeleteDir()
        {
            Directory.Delete(DirName, true);
        }

        private static void DeleteUser()
        {
            DirectoryEntry ad = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
            DirectoryEntries users = ad.Children;
            DirectoryEntry user = users.Find(UserName, "user");

            if (user != null)
            {
                users.Remove(user);
            }
        }

        private static void Impersonate()
        {
            if (LogonUser(UserName, ".", Password, (int)LogonType.LOGON32_LOGON_INTERACTIVE, (int)LogonProvider.LOGON32_PROVIDER_DEFAULT, ref LogonToken))
            {
                Identity = WindowsIdentity.Impersonate(LogonToken);
                return;
            }
        }

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(string lpszUserName,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);

        private static void SetAccess(string path, AccessControlType type)
        {
            FileSecurity fs = File.GetAccessControl(path);
            FileSystemAccessRule far = new FileSystemAccessRule(UserName, FileSystemRights.FullControl, type);
            fs.AddAccessRule(far);
            File.SetAccessControl(path, fs);
        }

        private static void UndoImpersonate()
        {
            if (Identity != null)
            {
                Identity.Undo();
                Identity = null;
            }

            if (LogonToken != IntPtr.Zero)
            {
                CloseHandle(LogonToken);
                LogonToken = IntPtr.Zero;
            }
        }
    }
}

运行这个程序的结果是:

File.Exists (with dir permissions): True
File.Exists (without dir permissions): False

谁能解释为什么他们不同?在这两种情况下,用户都没有文件的读取权限。

最佳答案

这是 File.Exist 的默认行为。根据MSDN :

File.Exist

Return Value Type: System.Boolean

true if the caller has the required permissions and path contains the name of an existing file; otherwise, false. This method also returns false if path is null, an invalid path, or a zero-length string. If the caller does not have sufficient permissions to read the specified file, no exception is thrown and the method returns false regardless of the existence of path.

另外

The Exists method should not be used for path validation, this method merely checks if the file specified in path exists. Passing an invalid path to Exists returns false.

也就是说,这里的required permission,就是需要知道文件存在的权限(顾名思义,File.Exist )。这意味着只要用户有权访问该目录,它就可以知道该文件是否存在

在给定目录权限的情况下,用户是否具有文件访问权限不会影响用户对文件存在的了解。但是没有目录权限,用户无法知道文件的存在,因此 File.Exist 返回 false


编辑(根据评论反馈):

可能最令人困惑的部分是最后一句话:

If the caller does not have sufficient permissions to read the specified file, no exception is thrown and the method returns false regardless of the existence of path.

读取指定文件的足够权限取决于目录的读取权限,而不是指定文件的读取权限。 (Rob 先生的补充评论)。 “足够”这个词可能会暗示它需要对目录进行读取访问的行为,不是对指定文件的读访问。

但我承认,解释和用词可能听起来违反直觉,因为人们可能会直观地将“足够的权限来读取指定文件”解释为对指定文件的读取权限而不是父目录。

关于c# - 当拒绝访问文件与拒绝访问目录时,File.Exists 的行为不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34890013/

相关文章:

c# - Visual Studio /C# : Debugging imported DLL

c# - 在 C# 中移动文件时遇到问题?

c# - 适用于 Windows-Identity 的 Asp.Net Core Intranet 应用程序中的模拟中间件

c# - 我可以在某些情况下关闭模拟吗

c# - 通过 SQL CLR 验证 NT 用户

c# - 在 WCF 中捕获第三方 DLL 事件

c# - 如何在 WPF 中操作另一个类的窗口对象

c# - IBMMQDotnet 客户端重试机制

带文件的 C 项目

c - 使用 fgets() 终止循环