Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

В моем случае ответ пришел к тому, какой уровень гранулярности разрешения мне нужен в Windows, по сравнению с Linux. В моем случае, я только забочусь о пользователях, группах и других разрешениях на Linux. В Windows основное чтение Read/Write All, оставшееся от DOS, достаточно для меня, то есть мне не нужно иметь дело с ACL в Windows.

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

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

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

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

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

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

Windows поддерживает то, что они называют "совместимой с ISO С++" версией 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, когда вы даете разрешение группе или другому (или удаляете ее в этом отношении), вы действительно меняете владельца. Если вы не хотите этого делать, но вам все равно не нужны полные разрешения ACL Windows, просто определите STRICT_UGO_PERMISSIONS.

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

  • Создайте непустой файл с соответствующими разрешениями в вашей системе.
  • Используйте Boost Fileystem copy_file, чтобы скопировать этот файл на нужный вывод.

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

Я просто нашел несколько способов сделать 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

EDIT:

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

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

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

Ответ 8

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