我有一个没有提升权限的进程,因此,我无法获取某些文件/目录的文件属性
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++) 的拷贝,(例如),我已经知道如何获取文件属性了。
我想知道路径是否是没有提升权限的文件/目录。
最佳答案
首先 GetLastError
返回的 ERROR_ACCESS_DENIED
并不总是意味着访问被拒绝错误。通常(但不总是)错误源 - 是 NTSTATUS
代码,由 native 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)。)- 这里什么也做不了。
但在具体情况下,我猜想操作实际上得到的不是 STATUS_ACCESS_DENIED
而是 STATUS_DELETE_PENDING
错误。如果有人调用 DeleteFile
但文件上仍然存在打开的句柄,则会出现此错误:
The
DeleteFile
function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls toCreateFile
to open the file fail withERROR_ACCESS_DENIED
.
(在这种情况下,NTSTATUS 代码当然是 STATUS_DELETE_PENDING
,但 RtlNtStatusToDosError
将其转换为 ERROR_ACCESS_DENIED
)
GetFileAttributes
失败的可能原因是该文件在关闭时标记为删除。但如果文件仍未删除,可以通过 FindFirstFileExW
获取它的属性.注意 - FindFirstFileExW
返回有关父文件夹中文件属性的信息。要调用此 api,我们只需要 FILE_LIST_DIRECTORY
访问父文件夹。这就是为什么如果我们有父文件夹的 FILE_LIST_DIRECTORY
,文件系统会为文件授予 FILE_READ_ATTRIBUTES
的原因。如果在页面文件上调用 GetFileAttributes
也可能会失败并出现错误 STATUS_SHARING_VIOLATION
(当然是非常特殊的情况)。如果我们没有 FILE_LIST_DIRECTORY
访问父文件夹(并且没有启用备份权限)- FindFirstFileExW
也会失败,这里什么也做不了。
如此可能的解决方案,为了不失败,当文件在关闭时标记为删除 - 在这种情况下调用 FindFirstFileExW
。请注意,与 GetFileAttributes
相比,FindFirstFileExW
的调用成本要高得多,从另一方面来看,当我们查询标记为删除的文件时,这种情况非常罕见。所以总是最好先调用 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);
}
}
关于c++ - 如何判断路径是文件还是没有提升权限的目录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52706859/