Search code examples
c++filesystemsstdfile-permissions

How to determine if the current user has read acesses to a file in C++?


Using std::filesystem::perms you can get the permissions set on a file. I want to check if I have access to read and write to a file. Using this method works if I am the owner of the file. If I am not the owner of the file (for example root owns it), then the perms returned isn't that useful for the task at hand without knowing the owner.

https://stackoverflow.com/a/7328340 explains how to get the file owner and the current user on Linux systems which can be used in conjunction with std::filesystem::perms for a solution on Linux that is not POSIX compliant.

Is there a way to check if the current user current user can read or write to a file in a POSIX compliant way? Can this be done using only the C++ std lib? What about with Boost?

https://stackoverflow.com/a/10682224 suggests that ownership can not be detected using boost. https://stackoverflow.com/a/59899055 suggests that interpreting the file permissions isn't always that meaningful, but I just want to know if I can read from the file before I attempt opening it.


Solution

  • POSIX rights consist of triads: an example -rw-rw-r--

    User rights could be obtained under UNIX by command "id User". For example:

    uid=1000(User) gid=1000(User) groups=1000(User),4(adm),7(lp),24(cdrom),27(sudo),30(dip)
    

    User has user id (uid) and belongs to several groups. Ability to read a file is based of uid and groups data. Reading could be done by a process and it also has uid/guid parameters

    There are many types of files, let's talk about simplest case about a regular file and not symlink. We need to check tree triads

    Checking what the selected user can read file is simple:

    // Set here proper info about user
    uid_t userId = getuid();
    gid_t groupId = getgid();
    
    std::string path = "myfile";
    std::filesystem::path filePath(path);
    
    auto status = std::filesystem::status(filePath);
    struct stat fileStat;
    stat(filePath.c_str(), &fileStat);
    
    if (fileStat.st_uid == userId) {
        // User is the same as the owner of the file
        if ((status.permissions() & std::filesystem::perms::owner_read) !=
            std::filesystem::perms::none) {
            return true; // User could read file based on first triple
        }
    }
    

    Checking rights to read file as others is even simplier:

        if ((status.permissions() & std::filesystem::perms::others_read) !=
             std::filesystem::perms::none) {
             return true; // User could read file based on third triple
         }
    

    Group processing is complicated, because we must check all groups user belongs to:

        // Get user name
    struct passwd *pw = getpwuid(userId);
    std::string username = pw->pw_name;
    
    // Get groups list
    std::vector<gid_t> groups;
    int ngroups = 0;
    uint result = getgrouplist(username.c_str(), groupId, nullptr, &ngroups);
    groups.resize(ngroups);
    result = getgrouplist(username.c_str(), groupId, groups.data(), &ngroups);
    
    // Check all groups
    for (uint i = 0; i < ngroups; ++i) {
        if (groups[i] == fileStat.st_gid) {
            // User belongs to a group that is equal to file group
            if ((status.permissions() & std::filesystem::perms::group_read)!= 
    std::filesystem::perms::none) {
                return true; // User could read file based on groups rights
            }
        }
    }
    

    This solution should work on Linux and BSD systems, but it isn't POSIX compatible

    There are also ACL and RBAC file permissions, they weren't checked. Those permissions aren't standard