我有一个没有提升特权的进程,因此,我无法获得某些文件/目录的File属性
const auto attr = GetFileAttributesW(path);
或
auto *pwfd = new WIN32_FIND_DATAW;
const auto handle = FindFirstFileW(path, pwfd);
在两种情况下,我都无法访问文件属性(因为我没有提升的权限)
但是我只需要知道path
是文件还是目录。
有没有办法知道有效路径是没有提升特权的文件还是目录?
编辑(例如),它不是How can I tell if a given path is a directory or a file? (C/C++)的副本,我已经知道如何获取文件属性。
我要询问是否知道该路径是没有提升特权的文件/目录。
答案 0 :(得分:1)
首先,ERROR_ACCESS_DENIED
返回的GetLastError
并不总是意味着访问被拒绝错误。通常(但并非总是)错误源-是NTSTATUS
代码,由本机api返回,然后通过RtlNtStatusToDosError
转换为win32错误代码。但是这种转换不是内射的。许多不同的NTSTATUS
代码不仅转换为ERROR_ACCESS_DENIED
,而且转换为相同的STATUS_ACCESS_DENIED
,结果经常导致关于错误真正原因的混淆。万一GetFileAttributes
,FindFirstFileExW
通话失败
extern "C" NTSYSAPI ULONG NTAPI RtlGetLastNtStatus();
api,而不是GetLastError()
。这没有记录,但是在某些情况下很有帮助。
关于具体的GetFileAttributesW
,现在-我们需要FILE_READ_ATTRIBUTES
访问文件,以免访问失败而被拒绝。但是文件系统将FILE_READ_ATTRIBUTES
授予调用者,或者如果文件安全描述符授予了它,则父文件夹授予了调用者FILE_LIST_DIRECTORY
。当然,如果我们没有同时为父文件夹FILE_LIST_DIRECTORY
和文件FILE_READ_ATTRIBUTES
以及调用者都没有SeBackupPrivilege
(此特权,系统将授予对任何文件的所有读取访问控制权,而不考虑为文件指定的访问控制列表(ACL)。)-在此无法执行任何操作。
但是在具体情况下,我认为op确实不是STATUS_ACCESS_DENIED
而是STATUS_DELETE_PENDING
错误。如果有人调用DeleteFile
但文件上仍然存在打开的句柄,就会出现此错误:
DeleteFile
函数将文件标记为关闭时要删除。 因此,直到删除的最后一个句柄都不会发生文件删除。 该文件已关闭。随后调用CreateFile
打开文件 失败ERROR_ACCESS_DENIED
。
(当然,NTSTATUS代码在这种情况下将是STATUS_DELETE_PENDING
,但是RtlNtStatusToDosError
会将其转换为ERROR_ACCESS_DENIED
)
GetFileAttributes
可能失败的可能原因是,该文件在关闭时被标记为删除。但是如果文件仍未删除,则可以通过FindFirstFileExW
获取它的属性。注意-FindFirstFileExW
返回有关父文件夹中文件属性的信息。对于调用此api,我们仅需要FILE_LIST_DIRECTORY
访问父文件夹。这就是如果我们的父文件夹有FILE_READ_ATTRIBUTES
时文件系统为文件授予FILE_LIST_DIRECTORY
的原因。如果在页面文件上调用GetFileAttributes
也会失败,并显示错误STATUS_SHARING_VIOLATION
(当然,这是非常特殊的情况)。如果我们没有FILE_LIST_DIRECTORY
对父文件夹的访问权限(并且没有启用备份权限)-FindFirstFileExW
也会失败,因此无法执行任何操作。
如此可行的解决方案(如果失败)是在关闭时标记为删除的文件-在这种情况下调用FindFirstFileExW
。请注意,另一方面,当我们查询标记为删除的文件时,FindFirstFileExW
的通话费用比GetFileAttributes
高得多。因此始终最好先调用GetFileAttributes
,并且只有在代码失败STATUS_DELETE_PENDING
的情况下才尝试FindFirstFileExW
调用。
NTSTATUS GetFileAttributesEx(PCWSTR FileName, DWORD* pdwFileAttributes)
{
DWORD dwFileAttributes = GetFileAttributes(FileName);
if (INVALID_FILE_ATTRIBUTES != dwFileAttributes)
{
*pdwFileAttributes = dwFileAttributes;
return STATUS_SUCCESS;
}
NTSTATUS status = RtlGetLastNtStatus();
switch (status)
{
case STATUS_SHARING_VIOLATION: // this is only for pagefile i think can be
case STATUS_DELETE_PENDING:
WIN32_FIND_DATAW fd;
HANDLE hFile = FindFirstFileExW(FileName, FindExInfoBasic, &fd, FindExSearchNameMatch, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
*pdwFileAttributes = fd.dwFileAttributes;
FindClose(hFile);
status = STATUS_SUCCESS;
}
else
{
status = RtlGetLastNtStatus();
}
break;
}
return status;
}
演示代码对此进行测试:
ULONG cb = 0, rcb = 0x80;
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
PWSTR FileName = 0;
do
{
if (cb < rcb)
{
cb = (ULONG)((PWSTR)stack - (FileName = (PWSTR)alloca((rcb - cb)* sizeof(WCHAR))));
}
rcb = ExpandEnvironmentStringsW(L"%tmp%/test.tmp", FileName, cb);
} while (cb < rcb);
if (rcb)
{
HANDLE hFile = CreateFile(FileName, DELETE, 0, 0, CREATE_NEW,
FILE_ATTRIBUTE_TEMPORARY|FILE_ATTRIBUTE_HIDDEN|FILE_FLAG_DELETE_ON_CLOSE, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
ULONG dwFileAttributes;
GetFileAttributesEx(FileName, &dwFileAttributes);
static FILE_DISPOSITION_INFO fdi = { TRUE };
SetFileInformationByHandle(hFile, FileDispositionInfo, &fdi, sizeof(fdi));
GetFileAttributesEx(FileName, &dwFileAttributes);
CloseHandle(hFile);
GetFileAttributesEx(FileName, &dwFileAttributes);
}
}