简体   繁体   English

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

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

I am using C++ ofstream to write out a file. 我正在使用C ++ ofstream来写出文件。 I want to set the permissions to be only accessible by the user: 700. In unix; 我想将权限设置为仅可由用户访问:700。 I suppose I can just issue a system("chmod 700 file.txt"); 我想我可以发布一个system("chmod 700 file.txt"); but I need this code to work on Windows as well. 但我也需要此代码才能在Windows上工作。 I can use some Windows api; 我可以使用一些Windows API; but what is the best c++ cross platform way to do this? 但是最好的c ++跨平台方法是什么?

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. 就我而言,答案取决于我在Windows和Linux上需要什么级别的权限粒度。 In my case, I only care about User, Group, and Other permission on Linux. 就我而言,我只关心Linux上的用户,组和其他权限。 On Windows, the basic Read/Write All permission leftover from DOS is good enough for me, ie I don't need to deal with ACL on Windows. 在Windows上,DOS剩下的基本“读取/写入所有”权限对我来说已经足够了,即,我不需要在Windows上处理ACL。

Generally speaking, Windows has two privilege models: the basic DOS model and the newer access control model. 一般来说,Windows有两种特权模型:基本的DOS模型和较新的访问控制模型。 Under the DOS model there is one type of privilege: write privilege. 在DOS模式下,有一种特权:写特权。 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. 否则不能。

The basic DOS model is sufficient for most Windows environments, ie environments where the system is used by a single user in a physical location that can be considered relatively secure. 基本的DOS模型足以满足大多数Windows环境的要求,即单个用户在可以认为相对安全的物理位置使用系统的环境。 The access control model is more complex by several orders of magnitude. 访问控制模型要复杂几个数量级。

The access control model uses access control lists (ACL) to grant privileges. 访问控制模型使用访问控制列表(ACL)授予特权。 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. 该模型不仅允许控制具有读取,写入和执行权限的用户,组和其他人,而且还允许控制网络上和Windows域之间的文件。 (You can also get this level of insanity on Unix systems with PAM.) (您也可以在带有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. 使用ACL麻烦很多。 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,而且还需要学习有关安全描述符,访问令牌和许多其他许多高级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. 我可以假装在Windows上设置权限,只要我真的在Linux上设置权限即可。

Windows supports what they call an "ISO C++ conformant" version of chmod(2). Windows支持chmod(2)的“ ISO C ++兼容”版本。 This API is called _chmod, and it is similar to chmod(2), but more limited and not type or name compatible (of course). 此API称为_chmod,它类似于chmod(2),但受限制得多,并且不兼容类型或名称(当然)。 Windows also has a deprecated chmod, so you can't simply add chmod to Windows and use the straight chmod(2) on Linux. 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. 请记住,我的解决方案仅提供DOS类型的安全性,这一点很重要。 This is also known as no security, but it is the amount of security that most apps give you on Windows. 这也称为无安全性,但这是大多数应用程序在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. 另外,在我的解决方案下,如果您未定义STRICT_UGO_PERMISSIONS,则当您授予对群组或其他权限(或为此删除该权限)时,您实际上是在更改所有者。 If you didn't want to do that, but you still didn't need full Windows ACL permissions, just define STRICT_UGO_PERMISSIONS. 如果您不想这样做,但仍然不需要完整的Windows ACL权限,只需定义STRICT_UGO_PERMISSIONS。

There is no cross-platform way to do this. 没有跨平台的方法可以做到这一点。 Windows does not support Unix-style file permissions. Windows不支持Unix样式的文件权限。 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. 为了执行所需的操作,您必须研究为该文件创建访问控制列表,这将使您可以显式定义用户和组的访问权限。

An alternative might be to create the file in a directory whose security settings have already been set to exclude everyone but the user. 另一种选择是在目录中创建该文件,该目录的安全性设置已经设置为排除用户以外的所有人。

Cross-platform example to set 0700 for a file with C++17 and its std::filesystem . 使用C ++ 17及其std::filesystem为文件设置0700的跨平台示例。

#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::perms and std::filesystem::perm_options . 请参阅std::filesystem::permissionsstd::filesystem::permsstd::filesystem::perm_options

The system() call is a strange beast. system()调用是一个奇怪的野兽。 I have been bitten by a NOP system() implementation on a Mac many moons ago. 许多个月前,我在Mac上被NOP system()实现所困扰。 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). 不幸的是,这也是在功能范围之外进行操作的唯一标准方法(在您的情况下-更改权限)。

Update: A proposed hack: 更新:建议的技巧:

  • Create a non-empty file with appropriate permissions on your system. 在系统上创建具有适当权限的非空文件。
  • Use Boost Filesystem's copy_file to copy this file out to your desired output. 使用Boost Filesystem的copy_file将此文件复制到所需的输出中。

    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. void copy_file(const path& frompath, const path& topath) :将void copy_file(const path& frompath, const path& topath)的文件的内容和属性复制到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. 因此,这不等同于UNIX中系统指定的cp命令。 It is also expected that the frompath variable would refer to a proper regular file. 还期望frompath变量将引用适当的常规文件。 Consider this example: frompath refers to a symbolic link /tmp/file1, which in turn refers to a file /tmp/file2; 考虑以下示例:frompath指向符号链接/ tmp / file1,而该链接又指向文件/ tmp / file2; topath is, say, /tmp/file3. 例如,topath是/ tmp / file3。 In this situation, copy_file will fail. 在这种情况下,copy_file将失败。 This is yet another difference that this API sports compared to the cp command. 与cp命令相比,此API还有另一个区别。

  • Now, overwrite the output with actual contents. 现在,用实际内容覆盖输出。

But, this is only a hack I thought of long after midnight. 但是,这只是我在午夜后很长时间才想到的一种破解方法。 Take it with a pinch of salt and try this out :) 把它和少许盐一起吃,然后试试看:)

I just found a couple of ways to do chmod 700 easily from the Windows command line. 我只是找到了几种从Windows命令行轻松执行chmod 700的方法。 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). 我将发布另一个问题,寻求帮助,以提供一个等效的win32安全描述符结构来使用(如果在接下来的几个小时内无法弄清)。

Windows 2000 & XP (messy- it always seems to prompt): Windows 2000和XP(混乱-总是出现提示):

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

Windows 2003+: Windows 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,您只需要擦除文件中的所有安全描述符,并在完全控制下为自己添加一个即可。

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

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. 在C ++中没有标准的方法来执行此操作,但是对于此特殊要求,您可能应该只使用#ifdef _WIN32编写自定义包装器。 Qt has a permission wrapper in it's QFile class, but this would of course mean depending on Qt ... Qt在其QFile类中具有权限包装,但这当然意味着要取决于Qt ...

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. 在Linux中,应该使用函数chmod(2)而不是使用system(2)来生成新的shell。 On Windows, you'll have to use the various authorization functions to make an ACL (access-control list) with the proper permissions. 在Windows上,您必须使用各种授权功能来制作具有适当权限的ACL(访问控制列表)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM