Как установить права доступа к файлам (кросс-платформенный) в C++?

Я использую C++ ofstream для записи файла. Я хочу, чтобы разрешения были доступны только пользователю: 700. В unix; Полагаю, я могу просто выдать a, system("chmod 700 file.txt"); но мне нужен этот код для работы и в Windows. Я могу использовать некоторый Windows api; но каков наилучший кроссплатформенный способ С ++ для этого?

Ответов (8)

Решение

По иронии судьбы, сегодня я столкнулся с той же самой потребностью.

В моем случае ответ сводился к тому, какой уровень детализации разрешений мне нужен в Windows по сравнению с Linux. В моем случае меня интересуют только разрешения «Пользователь», «Группа» и «Другой» в Linux. В Windows мне достаточно базовых разрешений на чтение / запись, оставшихся от DOS, т.е. мне не нужно иметь дело с ACL в Windows.

Вообще говоря, в Windows есть две модели привилегий: базовая модель DOS и новая модель управления доступом. В модели DOS существует один тип привилегий: привилегия записи. Все файлы могут быть прочитаны, поэтому нет возможности отключить разрешение на чтение (потому что оно не существует). Также отсутствует понятие разрешения на выполнение. Если файл можно прочитать (ответ «да») и он двоичный, то он может быть выполнен; иначе не может.

Базовой модели DOS достаточно для большинства сред Windows, то есть сред, в которых система используется одним пользователем в физическом месте, которое можно считать относительно безопасным. Модель контроля доступа сложнее на несколько порядков.

Модель управления доступом использует списки управления доступом (ACL) для предоставления привилегий. Привилегии могут быть предоставлены только процессом с необходимыми привилегиями. Эта модель не только позволяет управлять пользователями, группами и другими с разрешениями на чтение, запись и выполнение, но также позволяет управлять файлами по сети и между доменами Windows. (Вы также можете получить этот уровень безумия в системах Unix с PAM.)

Примечание. Модель контроля доступа доступна только для разделов NTFS, если вы используете разделы FAT, вы являетесь SOL.

Использование ACL - большая головная боль. Это нетривиальное мероприятие, и вам потребуется изучить не только ACL, но и все о дескрипторах безопасности, токенах доступа и многих других передовых концепциях безопасности Windows.

К счастью для меня, для моих текущих потребностей мне не нужна настоящая безопасность, которую обеспечивает модель контроля доступа. Я могу делать вид, будто устанавливаю разрешения в Windows, если я действительно устанавливаю разрешения в Linux.

Windows поддерживает то, что они называют «совместимой с ISO C++» версией chmod (2). Этот API называется _chmod, и он похож на chmod (2), но более ограничен и не совместим по типу или имени (конечно). В Windows также есть устаревший chmod, поэтому вы не можете просто добавить chmod в Windows и использовать прямой chmod (2) в Linux.

Я написал следующее:

#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

Важно помнить, что мое решение обеспечивает безопасность только типа DOS. Это также известно как отсутствие безопасности, но это уровень безопасности, который большинство приложений дает вам в Windows.

Кроме того, согласно моему решению, если вы не определяете STRICT_UGO_PERMISSIONS, когда вы даете разрешение группе или другому (или удаляете его, если на то пошло), вы действительно меняете владельца. Если вы не хотели этого делать, но вам по-прежнему не нужны полные разрешения Windows ACL, просто определите STRICT_UGO_PERMISSIONS.

Кросс-платформенный пример установки 0700 для файла с 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.
    }           
}

Смотрите std::filesystem::permissions, std::filesystem::permsи std::filesystem::perm_options.

Не знаю, сработает ли это, но вы можете изучить возможность использования chmod.exe исполняемого файла, поставляемого с Cygwin.

system() Вызов странный зверь. Много месяцев назад меня укусила реализация системы NOP () на Mac. Его реализация определяется, что означает, что стандарт не определяет, что должна делать реализация (платформа / компилятор). К сожалению, это также единственный стандартный способ сделать что-то, выходящее за рамки вашей функции (в вашем случае - изменение разрешений).

Обновление: предлагаемый взлом:

  • Создайте непустой файл с соответствующими разрешениями в вашей системе.

  • Используйте Boost Filesystem, copy_fileчтобы скопировать этот файл на желаемый результат.

    void copy_file(const path& frompath, const path& topath): Содержимое и атрибуты файла, на который ссылается frompath, копируются в файл, на который ссылается topath. Эта процедура ожидает, что целевой файл будет отсутствовать; если целевой файл присутствует, генерируется исключение. Следовательно, это не эквивалентно указанной системой команде cp в UNIX. Также ожидается, что переменная frompath будет ссылаться на правильный обычный файл. Рассмотрим этот пример: frompath относится к символической ссылке / tmp / file1, которая, в свою очередь, относится к файлу / tmp / file2; topath - это, скажем, / tmp / file3. В этой ситуации copy_file не удастся. Это еще одно отличие этого API от команды cp.

  • Теперь перезапишите вывод фактическим содержимым.

Но это всего лишь хитрость, о которой я подумал после полуночи. Возьмите это с щепоткой соли и попробуйте :)

В C++ нет стандартного способа сделать это, но для этого специального требования вам, вероятно, следует просто написать собственную оболочку с #ifdef _WIN32. Qt имеет обертку разрешений в классе QFile , но это, конечно, будет означать зависимость от Qt ...

Для этого нет кроссплатформенного способа. Windows не поддерживает права доступа к файлам в стиле Unix. Чтобы делать то, что вы хотите, вам нужно будет изучить создание списка управления доступом для этого файла, который позволит вам явно определять права доступа для пользователей и групп.

Альтернативой может быть создание файла в каталоге, параметры безопасности которого уже установлены таким образом, чтобы исключить всех, кроме пользователя.

Вы не можете сделать это кроссплатформенным способом. В Linux вы должны использовать эту функцию chmod(2)вместо использования system(2) для создания новой оболочки. В Windows вам придется использовать различные функции авторизации, чтобы создать ACL (список управления доступом) с соответствующими разрешениями.

Я только что нашел несколько способов легко выполнить chmod 700 из командной строки Windows. Я собираюсь опубликовать еще один вопрос с просьбой о помощи в разработке эквивалентной структуры дескриптора безопасности win32 для использования (если я не смогу понять это в ближайшие несколько часов).

Windows 2000 и XP (беспорядок - всегда подсказывает):

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

Windows 2003+:

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

РЕДАКТИРОВАТЬ:

Если у вас была возможность использовать ATL, эта статья описывает это (у меня нет Visual Studio): http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

На самом деле, в нем говорится, что образец кода также включает образец кода, не относящийся к ATL - в нем должно быть что-то, что работает для вас (и меня!)

Важно помнить, что r / w / x для владельца только на win32, вам просто нужно стереть все дескрипторы безопасности из файла и добавить один для себя с полным контролем.