web-dev-qa-db-ja.com

C ++で(プラットフォーム間で)ファイルのアクセス許可を設定する方法

C++ ofstreamを使用してファイルを書き出しています。アクセス許可をユーザーのみがアクセスできるように設定したい:700。 system("chmod 700 file.txt");を発行するだけでよいと思いますが、Windowsでもこのコードを使用する必要があります。一部のWindows APIを使用できます。しかし、これを行うための最良のC++クロスプラットフォーム方法は何ですか?

23
shergill

皮肉なことに、私は今日、この非常に同じニーズに出会ったばかりです。

私の場合、答えは、LinuxではなくWindowsで必要なアクセス許可の細分性のレベルに帰着しました。私の場合、私はLinuxでのユーザー、グループ、およびその他の権限のみに関心があります。 Windowsでは、DOSから残っている基本的なすべての読み取り/書き込み権限で十分です。つまり、WindowsでACLを処理する必要はありません。

一般的に、Windowsには2つの特権モデルがあります。基本的なDOSモデルと新しいアクセス制御モデルです。 DOSモデルでは、書き込み特権という1種類の特権があります。すべてのファイルを読み取ることができるため、読み取り許可をオフにする方法はありません(存在しないため)。実行権限の概念もありません。ファイルを読み取ることができ(答えはyes)、ファイルがバイナリの場合、実行できます。それ以外の場合はできません。

基本的なDOSモデルは、ほとんどのWindows環境、つまり、比較的安全であると考えられる物理的な場所で1人のユーザーがシステムを使用する環境には十分です。アクセス制御モデルは、数桁も複雑です。

アクセス制御モデルは、アクセス制御リスト(ACL)を使用して特権を付与します。特権は、必要な特権を持つプロセスによってのみ付与できます。このモデルでは、読み取り、書き込み、および実行のアクセス許可を持つユーザー、グループ、その他を制御できるだけでなく、ネットワークを介して、およびWindowsドメイン間でファイルを制御することもできます。 (PAMを備えたUnixシステムでは、このレベルの狂気を得ることができます。)

注:FATパーティションを使用している場合は、SOLである場合、アクセス制御モデルはNTFSパーティションでのみ使用できます。

ACLの使用は、お尻の大きな痛みです。これは簡単な作業ではなく、ACLだけでなく、セキュリティ記述子、アクセストークン、その他の多くの高度なWindowsセキュリティ概念についても学習する必要があります。

幸いなことに、現在のニーズでは、アクセス制御モデルが提供する真のセキュリティは必要ありません。 Linuxで実際にアクセス許可を設定している限り、基本的にWindowsでアクセス許可を設定するふりをして問題を解決できます。

Windowsは、「ISO C++準拠」バージョンのchmod(2)をサポートしています。このAPIは_chmodと呼ばれ、chmod(2)に似ていますが、より制限があり、タイプや名前の互換性はありません(もちろん)。 Windowsにも非推奨のchmodがあるため、chmodをWindowsに追加してLinuxでそのままのchmod(2)を使用することはできません。

私は以下を書きました:

#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を定義してください。

29
Mike

これを行うためのクロスプラットフォームの方法はありません。 Windowsは、Unixスタイルのファイル権限をサポートしていません。必要な操作を行うには、そのファイルのアクセス制御リストを作成する必要があります。これにより、ユーザーとグループのアクセス許可を明示的に定義できます。

別の方法として、ユーザー以外のすべてのユーザーを除外するようにセキュリティ設定が既に設定されているディレクトリにファイルを作成することもできます。

8
Ferruccio

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.
    }           
}

std::filesystem::permissionsstd::filesystem::perms および std::filesystem::perm_options を参照してください。

4
Roi Danton

system()呼び出しは奇妙な獣です。私は何ヶ月も前に、MacでのNOP system()実装に噛まれました。これは実装定義であり、実装(プラットフォーム/コンパイラ)が行うことを標準が定義していないことを意味します。残念ながら、これは、関数のスコープ外で何かを実行するための唯一の標準的な方法に関するものでもあります(あなたの場合-権限を変更します)。

pdate:提案されたハック:

  • システム上で適切な権限を持つ空でないファイルを作成します。
  • Boost Filesystemの_copy_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コマンドと比較してさらに異なる点です。

  • ここで、出力を実際の内容で上書きします。

しかし、これは私が真夜中にずっと考えていたハックにすぎません。塩ひとつまみでそれを取り、これを試してください:)

3
dirkgently

Windowsコマンドラインからchmod 700を簡単に実行する方法をいくつか見つけました。使用する同等の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サンプルコードも含まれていると書かれています-あなた(そして私!)

覚えておくべき重要なことは、win32でのみ所有者のr/w/xを取得することです。ファイルからすべてのセキュリティ記述子をワイプし、フルコントロールで自分用に追加する必要があります。

1
nstenz

うまくいくかどうかはわかりませんが、chmod.exe Cygwinに付属する実行可能ファイル。

1
codelogic

C++でこれを行う標準的な方法はありませんが、この特別な要件については、おそらく#ifdef _WIN32を使用してカスタムラッパーを記述する必要があります。 Qtには QFile クラスのパーミッションラッパーがありますが、これはもちろんQtに依存します...

1
VolkA

クロスプラットフォームの方法でそれを行うことはできません。 Linuxでは、chmod(2)を使用する代わりに、関数 system(2) を使用して新しいシェルを生成する必要があります。 Windowsでは、適切な権限を持つACL(アクセス制御リスト)を作成するために、さまざまな 認証関数 を使用する必要があります。

0
Adam Rosenfield