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

C# 当拒绝访问文件与拒绝访问目录时,File.Exists的行为不同,c#,file-io,impersonation,file-exists,C#,File Io,Impersonation,File Exists,根据File.Exists的属性,File.Exists方法应在出现任何错误时返回false,包括调用方无权读取文件 当文件被设置为FullControl拒绝给用户和FullControl拒绝给文件所在目录的用户时,我希望它返回false 我看到的是当用户可以访问目录而不是文件时,file.Exists返回true;但是,如果用户无法访问目录,File.Exists返回false 我写了一个小程序来演示我所说的: using System; using System.DirectoryServi

根据
File.Exists
的属性,
File.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

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

这是
文件.Exist的默认行为。根据:

File.Exist

返回值类型:
System.Boolean

如果调用者有 所需权限和路径包含现有用户的名称 文件否则,错误。如果路径为,则此方法也返回false null、无效路径或长度为零的字符串。如果打电话的人没有 有足够的权限读取指定的文件,无异常 被抛出,并且无论是否存在 路径

而且

Exists
方法不应用于路径验证,此方法 仅检查路径中指定的文件是否存在。通过一个病人 Exists的路径返回false

换句话说,此处的必需权限是知道文件存在的必需权限(正如方法名称所示,
file.Exist
)。这意味着,只要用户有权访问该目录,就可以知道该文件是否存在

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


编辑(评论反馈后):

可能最让人困惑的是最后一句话:

如果打电话的人没有 拥有读取指定文件的足够权限,无例外 被抛出,并且无论是否存在 路径

读取指定文件的足够权限取决于父目录的读取权限,而不是指定文件的读取权限。(先生的补充意见)。“足够”一词可能会给出一些行为提示,即它将仅依赖于对父目录的读访问,而不是对指定文件的读访问


但我承认,这种解释和用词的选择听起来可能有点违反直觉,因为人们可能会直觉地解释“有足够的权限阅读指定的文件”作为对指定文件而不是父目录的读取访问。

如果不是MSDN解释中的最后一句话,“如果调用方没有足够的权限读取指定文件,则不会引发异常,并且无论路径是否存在,该方法都返回false”,那么您的响应将非常有意义由于我明确删除了读访问权限,我希望它返回false…@Middas您是对的“足够的权限读取指定的文件”,这可能意味着可以访问目录或文件。但如果这意味着可以访问目录,我也会认为这样写是相当模糊的。请注意“足够”这个词。感谢您的解释,“足够”这个词非常违反直觉。我希望他们能将文章更新得更清楚。@Middas即将看到的文件取决于父目录的读取权限,而不是文件的读取权限。@Middas没问题。很高兴它能有所帮助。顺便提一下,好的问题和实验。这也给了我更好的理解