MSVC2015 上的实验性 C++17 文件系统:权限不一致

Posted

技术标签:

【中文标题】MSVC2015 上的实验性 C++17 文件系统:权限不一致【英文标题】:Experimental C++17 filesystem on MSVC2015: inconsistencies with permissions 【发布时间】:2016-01-23 16:21:58 【问题描述】:

我正在尝试使用实验性的 <filesystem> 库,它应该成为 C++17 的一部分(参见技术规范草案 N4100)MSVC2015(更新 1)。

我可以设法递归地显示目录的全部内容:

    using namespace std::tr2::sys;
    ...
    path dir = canonical(".");
    for (auto& p : recursive_directory_iterator(dir)) 
        if (is_regular_file(p))                
            cout << file_size(p);               
        perms pe = p.status().permissions();  // get authorisations
        cout <<"\t"<< (pe & perms::owner_read ? "r" : "-") // <== error should be allowed
            << (pe & perms::owner_write ? "w" : "-");
        cout <<"\t" << p << endl;     
    

问题和疑问:

    perms 对象上的按位 &amp;:编译器抱怨 C2440 error 缺少从 perms 到 bool 的转换。但这应该根据规范允许。这是一个错误还是我错过了什么?

    我通过将pe 转换为无符号来解决了这个问题。在那里我注意到,对于每个文件,包括我有意撤回写入授权的文件,该值始终为0xffff。是否会系统地为 Windows 上的权限返回一个恒定的虚拟值,或者我是否忘记了一些东西来获得有效的权限?

【问题讨论】:

我不确定权限,但我也注意到文件系统函数无法区分符号链接和常规文件和目录。 & 运算符仅在 enum 上有效,在 enum class 上无效。 0xffff 表示 perms::unknown。这还没有实现,在头文件中搜索_Todo 变量。文件系统严重偏向于 Unix 敏感性,甚至使用八进制常量,将 Windows 访问权限固定在它上面并不容易。 @HansPassant 说得通,谢谢。但是 perms 是一个必须遵守BitmaskType 的枚举类:所以它必须实现&amp;。与此同时,由于@Galik 的已删除答案,我发现我可以将 &-subexpression 转换为 bool,因此问题缩小到转换问题。然而,BitmaskType 定义声明结果应与 0 相当(==0 或 !=0)。不幸的是,这也是不可能的。是我对 BitmaskType 要求太高还是 MS 实现现在太不完整了? 引用:“或带有额外运算符重载的枚举(作用域和非作用域)”。没有重载。 【参考方案1】:

更新:我在下面所说的关于 Windows 权限的内容是正确的,但由于某种原因,即使是 FILE_ATTRIBUTE_READONLY 位似乎也不起作用。我会提交一个错误:)

作为目前的解决方法,您可以在生成的路径记录上执行status(),例如

#include <filesystem>
#include <iostream>
using namespace std;
using namespace std::tr2::sys;

int main() 
    path dir = canonical(".");
    for (auto& p : recursive_directory_iterator(dir)) 
        if (is_regular_file(p))
            cout << file_size(p);
        file_status stat = status(p); // Workaround bug in MSVC++2015
        perms pe = stat.permissions();  // get authorizations
        cout <<"\t"<< ((pe & perms::owner_read) != perms::none ? "r" : "-")
            << ((pe & perms::owner_write) != perms::none ? "w" : "-");
        cout <<"\t" << p << endl;
    

在我的系统上产生:

PS C:\Users\bion\Desktop\test> ..\fs.exe
68494859        r-      C:\Users\bion\Desktop\test\read-only.mp3
93805063        rw      C:\Users\bion\Desktop\test\read-write.mp3
PS C:\Users\bion\Desktop\test>

在 perms 对象上按位 &:编译器抱怨 C2440 错误,说明缺少从 perms 到 bool 的转换。但这应该根据规范允许。这是一个错误还是我错过了什么?

通过重载的operator&amp; 支持按位&amp;,应该可以正常工作。没有转换为布尔值。如果您想转换为布尔值,正确的做法是与enum class 声明的常量进行比较,例如:

if ((permsVar & perms::owner_read) != perms::none)

或使用该枚举类的直接初始化实例:

if ((permsVar & perms::owner_read) != perms)

[bitmask.types] 只说:

如果表达式 X & Y 不为零,则在对象 X 中设置值 Y。

并不是说文字 0 有一些转换可以用来与 enum class 进行比较。

我通过将 pe 转换为无符号来解决这个问题。在那里我注意到,对于每个文件,包括我故意撤回写入授权的文件,该值始终为 0xffff。是否会系统地为 Windows 上的权限返回一个恒定的虚拟值,或者我是否忘记了一些东西来获得有效的权限?

Windows 的权限模型确实与 Unix 权限模型没有相似之处——安全描述符确实具有所有者和组所有者值,但在 Windows DACL 之间映射没有普遍接受的约定(它可以表达比Unix 权限)和 Unix 权限。

因此,目前我们只查找 readonly 属性,或一组特定的属性,如果不是,我们“踢”并说权限未知。如果你想便携,你需要对未知常量做一些事情。

        // FILE STATUS FUNCTIONS
_FS_DLL _File_type __CLRCALL_PURE_OR_CDECL _Stat(const TCHAR *_Fname, _Perms *_Pmode)
       // get file status
    WIN32_FILE_ATTRIBUTE_DATA _Data;

    if (TFUN(GetFileAttributesEx)(_Fname, GetFileExInfoStandard, &_Data))
           // get file type and return permissions
        // !!! Here's where we check
        if (_Pmode != 0)
            *_Pmode = _Data.dwFileAttributes & FILE_ATTRIBUTE_READONLY
                ? READONLY_PERMS : perms::all;
        return (_Map_mode(_Data.dwFileAttributes));
        
    else
           // invalid, get error code
        // error mapping code omitted
        
    

您希望我们如何处理安全描述符,例如:

O:BAG:SYD:PAI(A;;WD;;;WD)(A;;FA;;;S-1-5-21-2127521184-1604012920-1887927527-9342113)(A;;0x4;;;BA)

上面写着:

O:BA:群主是BUILTIN\ADMINISTRATORSG:SY:群主是NT AUTHORITY\LOCAL SYSTEM。请注意,该组通常根本不在 Windows 权限中使用,并且仅为了与 Interix 兼容而存在,在这种情况下,Interix 是唯一读取或写入安全描述符的组,因此可以为所欲为。 :)D:PAI:DACL 为DACL_PROTECTED(不从父级继承权限)和DACL_AUTO_INHERIT(参与正常的权限继承系统;例如,此文件系统对象的子级将从这里继承权限)

(A;;WD;;;WD):授予WRITE_DAC(更改安全描述符的DACL部分的权限)对WORLD\EVERYONE的访问权限。嗯,WRITE_DAC 无法读取、写入或执行,那我们该怎么办? (A;;FA;;;S-1-5-21-2127521184-1604012920-1887927527-9342113):将FILE_ALL_ACCESS 授予REDMOND\bion。请注意,我不是所有者、组所有者或“其他人”,那么您将如何映射呢? (A;;0x4;;;BA)FILE_APPEND_DATA 授予BUILTIN\ADMINISTRATORS。这有点像写,但不允许您更改文件中已有的内容...

(而且我省略了一些诸如强制访问控制之类的东西......)

我并不是说我们不能尝试在此映射上做得更好,但如果您需要做一些重要的权限工作,&lt;filesystem&gt; TS 在支持访问控制列表的系统上并不能真正帮助您。

【讨论】:

嗨,Billy,如果您已经通过 Connect 提交了错误,可以在这里发布链接吗? 没有,我直接归档的。我现在要修复我们所有的 &lt;filesystem&gt; 错误 :) @ildjarn:我只是试图修复它;不要认为我们可以在更新中修复它,因为修复它会破坏我们的 DLL 的 ABI。 (错误是_Read_dir 不返回从msvcp140.dll 导出的权限位,因此它们获得了默认值)这可能是我为 Visual Studio 2015 之后的任何版本所做的第一件事是。 非常感谢您的跟进! 更新:我们的 C++17 版本的 将修复这个问题:D

以上是关于MSVC2015 上的实验性 C++17 文件系统:权限不一致的主要内容,如果未能解决你的问题,请参考以下文章

MSVC++14 上的 C++11 中的不可能的快速委托

Qt5 msvc 2010不识别自动(c ++ 11)[重复]

GCC C++14/17 成员函数指针模板参数的区别

C++17 函数模板中的静态数组初始化 (MSVC 2019)

lambda 如何在 MSVC2017 15.9.3 中使用 /std:c++17 中的静态局部错误返回值?

MSVC C++ 17 获得词法规范化路径