Windows 文件系统 API 不同的输出

Windows filesystem API different outputs

在我的工作场所,我们有一个封装在 Win32 文件系统 API 上的接口,它允许我们执行大量文件操作。因为我们有一个操作,它本质上是调用 GetFileAttributesExW 然后是 FindFirstFileExW.

这是一个简单的独立用例。

#include <Windows.h>
#include <string>
#include <iostream>
#include <errno.h>
#include <memory>

struct file_handle_close
{
    void operator()(void *handle) const
    {
        if (static_cast<HANDLE>(handle) != INVALID_HANDLE_VALUE)
        {
            CloseHandle(static_cast<HANDLE>(handle));
        }
    }
};

typedef std::unique_ptr<void, file_handle_close>  file_handle_uptr;

void function2(const std::wstring& path)
{
    WIN32_FILE_ATTRIBUTE_DATA attr_ctx;

    if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr_ctx) == 0)
    {    
        DWORD  error_code(GetLastError());
        std::cout << "Error occurred in GetFileAttributesExW : "<< error_code << std::endl;
    }
    else
    {     
        std::cout << "This is all good in GetFileAttributesExW!!!\n\n" ;
    }

}

void function3(const std::wstring& path)
{
    file_handle_uptr                         handle_uptr;
    WIN32_FIND_DATAW                     file_attr;
    DWORD                                error_code = ERROR_SUCCESS;

    handle_uptr.reset(FindFirstFileExW(path.c_str(), FindExInfoBasic, &file_attr, FindExSearchNameMatch, nullptr, 0));

    if (static_cast<HANDLE>(handle_uptr.get()) == INVALID_HANDLE_VALUE)
    {
        DWORD  error_code(GetLastError());
        std::cout << "Error occurred in FindFirstFileExW : "<< error_code << std::endl; 
    }
    else
    {
        std::cout << "This is all good in FindFirstFileExW!!!\n\n" ;
    }
}

int main(int argc,char** argv)
{
    if(argc < 2)
    {
        std::cout << "Please enter a path!!!"<<std::endl;
    }
    else
    {
        const std::string str = argv[1];
        const std::wstring wstr(str.begin(),str.end());

        function2(wstr);
        function3(wstr);
    }
    return 0;
}

我有两条混合权限的路径。

C:\TEMP\Dir1\Dir2 : Dir1 has permissions denied for current user
C:\TEMP\DIRX\DIRY\DIRZ : DIRX and DIRY have permissions denied for current user

我使用以下命令拒绝了权限

%icacls directoryname /deny username:(RX)

我将这些路径用作上述代码创建的 .exe 文件的输入。我得到以下输出。

B:\testfun>.\testfun.exe C:\TEMP

This is all good in GetFileAttributesExW!!!

This is all good in FindFirstFileExW!!!


B:\testfun>.\testfun.exe C:\TEMP\Dir1

This is all good in GetFileAttributesExW!!!

This is all good in FindFirstFileExW!!!


B:\testfun>.\testfun.exe C:\TEMP\Dir1\Dir2  <-------- This

This is all good in GetFileAttributesExW!!!

Error occurred in FindFirstFileExW : 5


B:\testfun>.\testfun.exe C:\TEMP\DIRX

This is all good in GetFileAttributesExW!!!

This is all good in FindFirstFileExW!!!


B:\testfun>.\testfun.exe C:\TEMP\DIRX\DIRY
Error occurred in GetFileAttributesExW : 5
Error occurred in FindFirstFileExW : 5


B:\testfun>.\testfun.exe C:\TEMP\DIRX\DIRY\DIRZ   <-------- This

This is all good in GetFileAttributesExW!!!

Error occurred in FindFirstFileExW : 5

我想了解的是这些函数的行为差异。

为什么 GetFileAttributesExW 给出不同的输出,而 FindFirstFileExW 似乎尊重目录上设置的权限?

我正在使用 Windows 10 Build 16299 和 VS 2017 编译器。

首先让我们看看 FindFirstFileExW use first parameter lpFileName - this is not final and exactly path to file which used to open. api is parse and split this string. it search for last trailing backslash \ in this string and split it to 2 strings. then it the first part of lpFileName (before last trailing backslash) if used as path to folder which system is try open with FILE_READ_DATA | SYNCHRONIZE access. and second part of lpFileName used (after wrap to UNICODE_STRING) in call NtQueryDirectoryFile 如何放置 FileName 参数 - 指向包含一个文件(或多个文件)名称的调用者分配的 Unicode 字符串的可选指针, 如果使用通配符) 在 FileHandle 指定的目录中。

例如,如果您调用 FindFirstFileExW(L"C:\TEMP\Dir1\Dir2", ..),则 C:\TEMP\Dir1\Dir2 拆分为 C:\TEMP\Dir1Dir2。系统尝试打开 C:\TEMP\Dir1(并在此处访问被拒绝)然后(如果打开正常)将在文件夹中搜索 Dir2(准确)文件 - 根本没有意义 - 通常我们使用通配符,例如,C:\TEMP\Dir1\*.

相反 GetFileAttributesEx 使用 lpFileName 作为文件名。

所以当你打电话时

FindFirstFileExW(L"C:\TEMP\Dir1\Dir2", ..);
GetFileAttributesEx(L"C:\TEMP\Dir1\Dir2", ..);

您测试了 2 个不同 个文件夹:

C:\TEMP\Dir1(第 1 行)和 C:\TEMP\Dir1\Dir2(第 2 行)- 访问结果可能不同并不奇怪。其次 - 此 api 使用 不同的 访问文件夹 - FindFirstFileExW 需要 FILE_READ_DATAGetFileAttributesEx 仅需要 FILE_READ_ATTRIBUTES 访问.所以由此也可以得到不同的结果。另请注意 FILE_READ_ATTRIBUTES 是文件系统的特殊访问权限。 ntfs(例如)在两种情况下授予调用者 FILE_READ_ATTRIBUTES 访问权限 - 如果调用者直接对文件具有 FILE_READ_ATTRIBUTES 访问权限,或者如果调用者对 父级具有 FILE_LIST_DIRECTORY 访问权限 文件夹(作为附注 - 与 DELETE 访问权限相同的情况 - 如果明确具有文件或(如果没有)如果我们对父文件夹具有 FILE_DELETE_CHILD 访问权限,我们可以获得它)。

if case 调用 C:\TEMP\Dir1 - GetFileAttributesEx 不会失败,因为您可以访问 FILE_LIST_DIRECTORY 父文件夹 C:\TEMPFindFirstFileExW 不会失败,因为它确实打开了您具有 FILE_READ_DATA 访问权限的 C:\TEMP(另请注意 FILE_READ_DATA == FILE_LIST_DIRECTORY == 1


C:\TEMP\DIRX

GetFileAttributesExWFindFirstFileExW 可以,因为用户在 C:\TEMP

上有 FILE_READ_DATA(与 FILE_LIST_DIRECTORY 相同)

C:\TEMP\DIRX\DIRY

GetFileAttributesExW 失败,因为用户在 DirY 上没有 FILE_READ_ATTRIBUTES 并且在 DirX 上没有 FILE_LIST_DIRECTORYFindFirstFileExW 失败,因为用户没有 FILE_READ_DATA DirX

C:\TEMP\DIRX\DIRY\DIRZ

GetFileAttributesExW 正常 - 用户拥有 DirZFILE_READ_ATTRIBUTES 访问权限。 FindFirstFileExW 失败,因为用户没有 FILE_READ_DATA DirY