如何在 C++ 中设置文件权限(跨平台)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/592448/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:08:32  来源:igfitidea点击:

How to set file permissions (cross platform) in C++?

c++windowsunix

提问by shergill

I am using C++ ofstreamto write out a file. I want to set the permissions to be only accessible by the user: 700. In unix; I suppose I can just issue a system("chmod 700 file.txt");but I need this code to work on Windows as well. I can use some Windows api; but what is the best c++ cross platform way to do this?

我正在使用 C++ofstream写出一个文件。我想将权限设置为只能由用户访问:700。在 unix 中;我想我可以只发出一个,system("chmod 700 file.txt");但我也需要这段代码在 Windows 上工作。我可以使用一些 Windows api;但最好的 C++ 跨平台方式是什么?

回答by Mike

Ironically, I have just run into this very same need earlier today.

具有讽刺意味的是,我今天早些时候刚刚遇到了同样的需求。

In my case, the answer came down to what level of permission granularity I need on Windows, versus Linux. In my case, I only care about User, Group, and Other permission on Linux. On Windows, the basic Read/Write All permission leftover from DOS is good enough for me, i.e. I don't need to deal with ACL on Windows.

就我而言,答案归结为我在 Windows 上需要什么级别的权限粒度,而不是在 Linux 上。就我而言,我只关心 Linux 上的用户、组和其他权限。在 Windows 上,DOS 剩下的基本读/写所有权限对我来说已经足够了,即我不需要在 Windows 上处理 ACL。

Generally speaking, Windows has two privilege models: the basic DOS model and the newer access control model. Under the DOS model there is one type of privilege: write privilege. All files can be read, so there is no way to turn off read permission (because it doesn't exist). There is also no concept of execute permission. If a file can be read (answer is yes) and it is binary, then it can be executed; otherwise it can't.

一般来说,Windows 有两种特权模型:基本的 DOS 模型和较新的访问控制模型。在 DOS 模式下,有一种权限:写权限。所有文件都可以读取,所以没有办法关闭读取权限(因为它不存在)。也没有执行权限的概念。如果一个文件可以读取(答案是肯定的)并且是二进制的,那么它可以被执行;否则不能。

The basic DOS model is sufficient for most Windows environments, i.e. environments where the system is used by a single user in a physical location that can be considered relatively secure. The access control model is more complex by several orders of magnitude.

基本的 DOS 模型足以满足大多数 Windows 环境,即系统由单个用户在相对安全的物理位置使用的环境。访问控制模型要复杂几个数量级。

The access control model uses access control lists (ACL) to grant privileges. Privileges can only be granted by a process with the necessary privileges. This model not only allows the control of User, Group, and Other with Read, Write, and Execute permission, but it also allows control of files over the network and between Windows domains. (You can also get this level of insanity on Unix systems with PAM.)

访问控制模型使用访问控制列表 (ACL) 来授予权限。权限只能由具有必要权限的进程授予。此模型不仅允许控制具有读取、写入和执行权限的用户、组和其他,而且还允许通过网络和 Windows 域之间控制文件。(你也可以在带有 PAM 的 Unix 系统上达到这种程度的疯狂。)

Note: The Access Control model is only available on NTFS partitions, if you are using FAT partitions you are SOL.

注意:访问控制模型仅适用于 NTFS 分区,如果您使用 FAT 分区,则您是 SOL。

Using ACL is a big pain in the ass. It is not a trivial undertaking and it will require you to learn not just ACL but also all about Security Descriptors, Access Tokens, and a whole lot of other advanced Windows security concepts.

使用 ACL 是一个很大的麻烦。这不是一项微不足道的任务,它不仅需要您学习 ACL,还需要学习所有关于安全描述符、访问令牌和许多其他高级 Windows 安全概念的知识。

Fortunately for me, for my current needs, I don't need the true security that the access control model provides. I can get by with basically pretending to set permissions on Windows, as long as I really set permissions on Linux.

幸运的是,对于我当前的需求,我不需要访问控制模型提供的真正安全性。只要我真的在 Linux 上设置了权限,我基本上可以假装在 Windows 上设置权限。

Windows supports what they call an "ISO C++ conformant" version of chmod(2). This API is called _chmod, and it is similar to chmod(2), but more limited and not type or name compatible (of course). Windows also has a deprecated chmod, so you can't simply add chmod to Windows and use the straight chmod(2) on Linux.

Windows 支持他们所谓的“符合 ISO C++ 标准”的 chmod(2) 版本。这个 API 被称为 _chmod,它类似于 chmod(2),但更受限制并且不兼容类型或名称(当然)。Windows 也有一个已弃用的 chmod,因此您不能简单地将 chmod 添加到 Windows 并在 Linux 上直接使用 chmod(2)。

I wrote the following:

我写了以下内容:

#include <sys/stat.h>
#include <sys/types.h>

#ifdef _WIN32
#   include <io.h>

typedef int mode_t;

/// @Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
///       of User, Group, or Other will set Read for User and setting Write
///       will set Write for User.  Otherwise, Read and Write for Group and
///       Other are ignored.
///
/// @Note For the POSIX modes that do not have a Windows equivalent, the modes
///       defined here use the POSIX values left shifted 16 bits.

static const mode_t S_ISUID      = 0x08000000;           ///< does nothing
static const mode_t S_ISGID      = 0x04000000;           ///< does nothing
static const mode_t S_ISVTX      = 0x02000000;           ///< does nothing
static const mode_t S_IRUSR      = mode_t(_S_IREAD);     ///< read by user
static const mode_t S_IWUSR      = mode_t(_S_IWRITE);    ///< write by user
static const mode_t S_IXUSR      = 0x00400000;           ///< does nothing
#   ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWGRP      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = mode_t(_S_IREAD);     ///< read by *USER*
static const mode_t S_IWOTH      = mode_t(_S_IWRITE);    ///< write by *USER*
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   else
static const mode_t S_IRGRP      = 0x00200000;           ///< does nothing
static const mode_t S_IWGRP      = 0x00100000;           ///< does nothing
static const mode_t S_IXGRP      = 0x00080000;           ///< does nothing
static const mode_t S_IROTH      = 0x00040000;           ///< does nothing
static const mode_t S_IWOTH      = 0x00020000;           ///< does nothing
static const mode_t S_IXOTH      = 0x00010000;           ///< does nothing
#   endif
static const mode_t MS_MODE_MASK = 0x0000ffff;           ///< low word

static inline int my_chmod(const char * path, mode_t mode)
{
    int result = _chmod(path, (mode & MS_MODE_MASK));

    if (result != 0)
    {
        result = errno;
    }

    return (result);
}
#else
static inline int my_chmod(const char * path, mode_t mode)
{
    int result = chmod(path, mode);

    if (result != 0)
    {
        result = errno;
    }

    return (result);
}
#endif

It's important to remember that my solution only provides DOS type security. This is also known as no security, but it is the amount of security that most apps give you on Windows.

请务必记住,我的解决方案仅提供 DOS 类型的安全性。这也称为无安全性,但这是大多数应用程序在 Windows 上为您提供的安全性。

Also, under my solution, if you don't define STRICT_UGO_PERMISSIONS, when you give a permission to group or other (or remove it for that matter), you are really changing the owner. If you didn't want to do that, but you still didn't need full Windows ACL permissions, just define STRICT_UGO_PERMISSIONS.

另外,在我的解决方案下,如果您没有定义 STRICT_UGO_PERMISSIONS,那么当您授予组或其他权限(或为此将其删除)时,您实际上是在更改所有者。如果您不想这样做,但您仍然不需要完整的 Windows ACL 权限,只需定义 STRICT_UGO_PERMISSIONS。

回答by Ferruccio

There is no cross-platform way to do this. Windows does not support Unix-style file permissions. In order to do what you want, you'll have to look into creating an access control list for that file, which will let you explicitly define access permissions for users and groups.

没有跨平台的方法可以做到这一点。Windows 不支持 Unix 风格的文件权限。为了执行您想要的操作,您必须考虑为该文件创建一个访问控制列表,这将允许您明确定义用户和组的访问权限。

An alternative might be to create the file in a directory whose security settings have already been set to exclude everyone but the user.

另一种方法可能是在安全设置已设置为排除除用户之外的所有人的目录中创建文件。

回答by Roi Danton

Cross-platform example to set 0700 for a file with C++17 and its std::filesystem.

使用 C++17 及其std::filesystem.

#include <exception>
//#include <filesystem>
#include <experimental/filesystem> // Use this for most compilers as of yet.

//namespace fs = std::filesystem;
namespace fs = std::experimental::filesystem; // Use this for most compilers as of yet.

int main()
{
    fs::path myFile = "path/to/file.ext";
    try {
        fs::permissions(myFile, fs::perms::owner_all); // Uses fs::perm_options::replace.
    }
    catch (std::exception& e) {
        // Handle exception or use another overload of fs::permissions() 
        // with std::error_code.
    }           
}

See std::filesystem::permissions, std::filesystem::permsand std::filesystem::perm_options.

std::filesystem::permissions,std::filesystem::permsstd::filesystem::perm_options

回答by dirkgently

The system()call is a strange beast. I have been bitten by a NOP system() implementation on a Mac many moons ago. It's implementation defined meaning the standard doesn't define what an implementation (platform/compiler) is supposed to do. Unfortunately, this is also about the only standard way of doing something outside the scope of your function (in your case -- changing the permissions).

system()呼叫是一个奇怪的野兽。多年前,我被 Mac 上的 NOP system() 实现所困扰。它是实现定义的,意味着标准没有定义实现(平台/编译器)应该做什么。不幸的是,这也是在您的功能范围之外执行某些操作的唯一标准方法(在您的情况下 - 更改权限)。

Update:A proposed hack:

更新:建议的黑客攻击:

  • Create a non-empty file with appropriate permissions on your system.
  • Use Boost Filesystem's copy_fileto copy this file out to your desired output.

    void copy_file(const path& frompath, const path& topath): The contents and attributes of the file referred to by frompath is copied to the file referred to by topath. This routine expects a destination file to be absent; if the destination file is present, it throws an exception. This, therefore, is not equivalent to the system specified cp command in UNIX. It is also expected that the frompath variable would refer to a proper regular file. Consider this example: frompath refers to a symbolic link /tmp/file1, which in turn refers to a file /tmp/file2; topath is, say, /tmp/file3. In this situation, copy_file will fail. This is yet another difference that this API sports compared to the cp command.

  • Now, overwrite the output with actual contents.

  • 在您的系统上创建一个具有适当权限的非空文件。
  • 使用 Boost Filesystem'scopy_file将此文件复制到您想要的输出。

    void copy_file(const path& frompath, const path& topath): 将frompath 引用的文件的内容和属性复制到topath 引用的文件中。该例程期望目标文件不存在;如果目标文件存在,则抛出异常。因此,这不等同于 UNIX 中系统指定的 cp 命令。还期望 frompath 变量将引用正确的常规文件。考虑这个例子:frompath 指向一个符号链接/tmp/file1,它又指向一个文件/tmp/file2;例如,topath 是 /tmp/file3。在这种情况下,copy_file 将失败。这是该 API 与 cp 命令相比的另一个不同之处。

  • 现在,用实际内容覆盖输出。

But, this is only a hack I thought of long after midnight. Take it with a pinch of salt and try this out :)

但是,这只是我在午夜之后想到的一个黑客。用一撮盐把它拿来试试看:)

回答by nstenz

I just found a couple of ways to do chmod 700easily from the Windows command line. I'm going to post another question asking for help coming up with an equivalent win32 security descriptor structure to use (if I can't figure it out in the next few hours).

我刚刚找到了几种从 Windows 命令行轻松执行chmod 700的方法。我将发布另一个问题,寻求帮助提出要使用的等效 win32 安全描述符结构(如果我在接下来的几个小时内无法弄清楚)。

Windows 2000 & XP (messy- it always seems to prompt):

Windows 2000 & XP(乱七八糟 - 它似乎总是提示):

echo Y|cacls *onlyme.txt* /g %username%:F

Windows 2003+:

视窗 2003+:

icacls *onlyme.txt* /inheritance:r /grant %username%:r

EDIT:

编辑:

If you had the ability to use the ATL, this article covers it (I don't have Visual Studio available): http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

如果您有能力使用 ATL,本文将介绍它(我没有可用的 Visual Studio):http: //www.codeproject.com/KB/winsdk/accessctrl2.aspx

Actually, it says the sample code includes non-ATL sample code as well- it should have something that works for you (and me!)

实际上,它说示例代码也包括非 ATL 示例代码——它应该有一些适合你(和我!)

The important thing to remember is to get r/w/x for owner only on win32, you just have to wipe all of the security descriptors from the file and add one for yourself with full control.

要记住的重要一点是,仅在 win32 上为所有者获取 r/w/x,您只需从文件中擦除所有安全描述符,并为自己添加一个完全控制的描述符。

回答by codelogic

No idea if it would work, but you could look into using the chmod.exeexecutable that comes with Cygwin.

不知道它是否可行,但您可以考虑使用chmod.exeCygwin 附带的可执行文件。

回答by VolkA

There's no standard way to do this in C++, but for this special requirement you should probably just write a custom wrapper, with #ifdef _WIN32. Qt has a permission wrapper in it's QFileclass, but this would of course mean depending on Qt ...

在 C++ 中没有标准的方法来做到这一点,但对于这个特殊的要求,你可能应该编写一个自定义包装器,使用 #ifdef _WIN32。Qt 在它的QFile类中有一个权限包装器,但这当然意味着取决于 Qt ...

回答by Adam Rosenfield

You can't do it in a cross-platform manner. In Linux, you should use the function chmod(2)instead of using system(2)to spawn a new shell. On Windows, you'll have to use the various authorization functionsto make an ACL (access-control list) with the proper permissions.

你不能以跨平台的方式做到这一点。在 Linux 中,您应该使用该函数chmod(2)而不是使用system(2)来生成新的 shell。在 Windows 上,您必须使用各种授权功能来创建具有适当权限的 ACL(访问控制列表)。