当文件被拒绝访问与目录被拒绝访问时,File.Exists 的行为不同
Posted
技术标签:
【中文标题】当文件被拒绝访问与目录被拒绝访问时,File.Exists 的行为不同【英文标题】:File.Exists acts differently when access is denied to the file vs denied to the dir 【发布时间】:2016-04-25 16:57:54 【问题描述】:基于File.Exists
的MSDN documentation,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
谁能解释他们为什么不同?在这两种情况下,用户都没有文件的读取权限。
【问题讨论】:
【参考方案1】:这是File.Exist
的默认行为。根据MSDN:
File.Exist
返回值类型:
System.Boolean
true 如果调用者有 必需的权限和路径包含现有的名称 文件;否则为假。如果 path 是,此方法也返回 false null、无效路径或零长度字符串。如果调用者不 有足够的权限读取指定的文件,也不例外 被抛出并且该方法返回 false 而不管是否存在 路径。
另外
Exists
方法不应该用于路径验证,这个方法 仅检查 path 中指定的文件是否存在。传递无效 Exists 的路径返回 false。
换句话说,这里的所需权限,是知道文件存在所需的权限(正如方法名称所暗示的,File.Exist
)。这意味着只要用户有权访问该目录,它就可以知道该文件是否存在。
在给定目录权限的情况下,用户是否具有文件访问权限并不影响用户对文件存在的认识。但是没有目录权限,用户无法知道文件的存在,因此File.Exist
返回false
编辑(根据 cmets 的反馈):
可能相当令人困惑的部分是最后一句话:
如果调用者没有 有足够的权限读取指定的文件,也不例外 被抛出并且该方法返回 false 而不管是否存在 路径。
读取指定文件的足够权限取决于父目录的读取权限,而不是指定文件的读取权限。 (Rob先生的补充评论)。 “足够”一词可能会暗示其行为将仅取决于需要对 父目录的读取访问权限,不对指定文件的读取权限。
但我承认,解释和用词可能听起来相当反直觉,因为人们可能会直观地将“读取指定文件的足够权限”解释为对指定文件的读取权限而不是到父目录。
【讨论】:
如果不是MSDN解释中的最后一句,您的响应将非常有意义,“如果调用者没有足够的权限来读取指定的文件,则不会抛出异常并且方法无论路径是否存在,都返回 false。"由于我明确删除了读取访问权限,我希望它返回 false... @Middas 你是对的“读取指定文件的足够权限”这可能意味着可以访问目录或可以访问文件。但如果这意味着可以访问目录,我也会认为这样写是相当含糊的。注意“足够”这个词。 感谢您的解释,“足够”这个词非常违反直觉。我希望他们能更新文章以使其更加清晰。 @Middas 要查看文件取决于父目录的读取权限,而不是文件的读取权限。 @Middas 没问题。很高兴它可以提供任何帮助。顺便说一句,好问题和实验。这也让我更好地理解。 :)【参考方案2】:如果文件确实存在,但File.Exists(filePath)
返回false,则说明应用程序对目录和文件都没有有读权限,可以证明。
如果你真的想测试一个文件是否存在,你可以调用File.GetAccessControl(filePath)
。如果调用无异常返回,则说明文件确实存在。
【讨论】:
以上是关于当文件被拒绝访问与目录被拒绝访问时,File.Exists 的行为不同的主要内容,如果未能解决你的问题,请参考以下文章