【问题标题】:How to tell if a path is a file or a directory without elevated privileges如何判断路径是文件还是没有提升权限的目录
【发布时间】:2018-10-08 16:51:40
【问题描述】:

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

我想知道路径是否是没有提升权限的文件/目录。

【问题讨论】:

  • 不,它不是重复的,我在我的问题中提到了GetFileAttributesW。我问的是提升特权。不一样
  • 你是怎么得到path的? GetFileAttributesW 只有在您没有父文件夹的读取权限时才会失败。
  • GetFileAttributesW 返回的错误是什么?共享违规? FindFirstFileW 返回的错误是什么?
  • FindFirstFileW 在这里更强大,因为GetFileAttributesW 可以返回ERROR_SHARING_VIOLATION,但在这种情况下FindFirstFileW 就可以了——你需要的一切——访问父文件夹进行读取
  • GetFileAttributesW 返回 ERROR_ACCESS_DENIED 其中 FindFirstFileW 能够返回有效属性 - 对此非常怀疑。对于GetFileAttributesW,我们只需要FILE_READ_ATTRIBUTES 访问。但这是一种特殊的访问权限——如果调用者拥有FILE_LIST_DIRECTORY 对父级的访问权限,则 ntfs 会授予它。但如果您没有此访​​问权限 - FindFirstFileW 将失败

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


【解决方案1】:

首先由GetLastError 返回的ERROR_ACCESS_DENIED 并不总是意味着拒绝访问错误。通常(但并非总是)错误源 - 是 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。请注意,FindFirstFileExWGetFileAttributes 相比,调用成本要高得多,从另一方面来看,当我们查询标记为删除的文件时,这种情况非常罕见。所以总是最好先调用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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-13
    • 2012-07-17
    • 2010-11-03
    • 1970-01-01
    • 2017-05-01
    • 2012-04-19
    • 2013-04-28
    • 1970-01-01
    相关资源
    最近更新 更多