如何判断路径是没有提升特权的文件还是目录

时间:2018-10-08 16:51:41

标签: c++ c++11 winapi filesystems

我有一个没有提升特权的进程,因此,我无法获得某些文件/目录的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++)的副本,我已经知道如何获取文件属性。

我要询问是否知道该路径是没有提升特权的文件/目录。

1 个答案:

答案 0 :(得分:1)

首先,ERROR_ACCESS_DENIED返回的GetLastError并不总是意味着访问被拒绝错误。通常(但并非总是)错误源-是NTSTATUS代码,由本机api返回,然后通过RtlNtStatusToDosError转换为win32错误代码。但是这种转换不是内射的。许多不同的NTSTATUS代码不仅转换为ERROR_ACCESS_DENIED,而且转换为相同的STATUS_ACCESS_DENIED,结果经常导致关于错误真正原因的混淆。万一GetFileAttributesFindFirstFileExW通话失败

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);
    }
}