acl_dump

なんとかACLからCygwinっぽいパーミッション情報を引き出すことができました。
ただ、実際に使ってみるとあまり甘みがないことが分かり、ボツになりました。
例えばWindowsのホームディレクトリをCygwinで見ると、

$ ls -ld /cygdrive/c/Documents\ and\ Settings/hamigaki
d---rwx---+ 23 Administrators SYSTEM 0 Nov 15 23:47 /cygdrive/c/Documents and Settings/hamigaki

ディレクトリのオーナーがグループになっています。
オーナーのパーミッションが一つもないのは、パーミッションはユーザーhamigakiに対して設定されているためです。
このディレクトリをtarにしてUNIXに持っていってた場合、パーミッションを保存してもほとんど意味がありません。
そもそもユーザーとグループをきちんとマッピングできなければパーミッションは役に立たないのです。


そんな訳でACL->パーミッション変換はボツになりましたが、テストに使ったコードだけでも貼っておきます。

#define _WIN32_WINNT 0x0500
#pragma comment(lib, "advapi32.lib")

#include <boost/shared_ptr.hpp>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>

#include <windows.h>
#include <aclapi.h>
#include <sddl.h>

// shared_ptr用
template<class T>
struct local_free
{
    void operator()(T* p) const
    {
        ::LocalFree(p);
    }
};

// SECURITY_INFORMATIONフラグ
struct security_info_flags
{
    static const unsigned long owner = OWNER_SECURITY_INFORMATION;
    static const unsigned long group = GROUP_SECURITY_INFORMATION;
    static const unsigned long dacl = DACL_SECURITY_INFORMATION;
    static const unsigned long sacl = SACL_SECURITY_INFORMATION;

    static const unsigned long normal = owner | group | dacl;
    static const unsigned long all = owner | group | dacl | sacl;
};

// セキュリティ情報
class security_info
{
public:
    security_info(::SECURITY_DESCRIPTOR* desc,
            ::SID* owner, ::SID* group, ::ACL* dacl, ::ACL* sacl)
        : pimpl_(desc, local_free< ::SECURITY_DESCRIPTOR>())
        , owner_(owner), group_(group), dacl_(dacl), sacl_(sacl)
    {
    }

    ::SID* owner() const { return owner_; }
    ::SID* group() const { return group_; }
    ::ACL* dacl() const { return dacl_; }
    ::ACL* sacl() const { return sacl_; }

private:
    boost::shared_ptr< ::SECURITY_DESCRIPTOR> pimpl_;
    ::SID* owner_;
    ::SID* group_;
    ::ACL* dacl_;
    ::ACL* sacl_;
};

// ファイルのセキュリティ情報を取得する
security_info
get_file_security_info(
    const char* filename, unsigned long flags = security_info_flags::normal)
{
    ::PSECURITY_DESCRIPTOR desc;
    ::PSID owner;
    ::PSID group;
    ::ACL* dacl;
    ::ACL* sacl;

    // GetNamedSecurityInfoA()の引数が非constなので、念のためコピー
    std::vector<char> name(std::strlen(filename)+1);
    std::strcpy(&name[0], filename);

    ::DWORD res = ::GetNamedSecurityInfoA(
        &name[0], ::SE_FILE_OBJECT, flags, &owner, &group, &dacl, &sacl, &desc);

    if (res != ERROR_SUCCESS)
        throw std::runtime_error("failed GetNamedSecurityInfoA()");

    return security_info(
        static_cast< ::SECURITY_DESCRIPTOR*>(desc),
        static_cast< ::SID*>(owner),
        static_cast< ::SID*>(group), dacl, sacl);
}

// SIDをテキストSIDに変換する
std::string to_string(::SID* p)
{
    char* s;
    ::ConvertSidToStringSid(p, &s);
    boost::shared_ptr<char> sp((s), local_free<char>());
    return std::string(s);
}

bool equal_sid(::SID* lhs, ::SID* rhs)
{
    return ::EqualSid(lhs, rhs) != 0;
}

// ACLからPOSIXパーミッションを生成する
unsigned to_permissions(
    ::SID* sid, ::SID* owner, ::SID* group,
    ::ACCESS_MASK mask)
{
    unsigned tmp = 0;
    if ((mask & GENERIC_ALL) != 0)
        tmp |= 07;

    if ((mask & GENERIC_READ) != 0)
        tmp |= 04;
    if ((mask & GENERIC_WRITE) != 0)
        tmp |= 02;
    if ((mask & GENERIC_EXECUTE) != 0)
        tmp |= 01;

    if ((mask & FILE_GENERIC_READ) == FILE_GENERIC_READ)
        tmp |= 04;
    if ((mask & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE)
        tmp |= 02;
    if ((mask & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE)
        tmp |= 01;

    ::SID c_owner = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, 0 };
    ::SID c_group = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, 1 };
    ::SID everyone = { 1, 1, SECURITY_WORLD_SID_AUTHORITY, 0 };

    if (equal_sid(sid, owner) || equal_sid(sid, &c_owner))
        return tmp << 6;
    else if (equal_sid(sid, group) || equal_sid(sid, &c_group))
        return tmp << 3;
    else if (equal_sid(sid, &everyone))
        return tmp;
    else
        return 0;
}

// AceFlagsのダンプ
std::string ace_flags_to_string(unsigned b)
{
    b &= VALID_INHERIT_FLAGS;

    std::ostringstream os;
    if ((b & OBJECT_INHERIT_ACE) != 0)
        os << "OBJECT_INHERIT_ACE ";
    if ((b & CONTAINER_INHERIT_ACE) != 0)
        os << "CONTAINER_INHERIT_ACE ";
    if ((b & NO_PROPAGATE_INHERIT_ACE) != 0)
        os << "NO_PROPAGATE_INHERIT_ACE ";
    if ((b & INHERIT_ONLY_ACE) != 0)
        os << "INHERIT_ONLY_ACE ";
    if ((b & INHERITED_ACE) != 0)
        os << "INHERITED_ACE ";
    return os.str();
}

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 2)
        {
            std::cerr << "Usage: dump_acl (file)" << std::endl;
            return 1;
        }

        const security_info& info = get_file_security_info(argv[1]);

        std::cout << "owner=" << to_string(info.owner()) << '\n';
        std::cout << "group=" << to_string(info.group()) << '\n';

        ::ACL_SIZE_INFORMATION sizes;
        ::GetAclInformation(
            info.dacl(), &sizes, sizeof(sizes), AclSizeInformation);

        ::DWORD count = sizes.AceCount;
        unsigned denied = 0;
        unsigned allowed = 0;
        for (::DWORD i = 0; i < count; ++i)
        {
            std::cout << "----------------------------------------\n";

            void* ptr;
            ::GetAce(info.dacl(), i, &ptr);
            ::ACE_HEADER* head = static_cast< ::ACE_HEADER*>(ptr);

            std::cout
                << "type="
                << static_cast<unsigned>(head->AceType) << '\n';
            std::cout
                << "flags=" << ace_flags_to_string(head->AceFlags) << '\n';

            if (head->AceType == ACCESS_ALLOWED_ACE_TYPE)
            {
                ::ACCESS_ALLOWED_ACE* ace =
                    static_cast< ::ACCESS_ALLOWED_ACE*>(ptr);

                std::cout
                    << "mask=0x"
                    << std::hex << ace->Mask << std::dec << '\n';

                ::SID* sid =
                    reinterpret_cast< ::SID*>(&(ace->SidStart));

                std::cout << "sid=" << to_string(sid) << '\n';

                if ((head->AceFlags & INHERIT_ONLY_ACE) == 0)
                {
                    allowed |= to_permissions(
                        sid, info.owner(), info.group(), ace->Mask);
                }
            }
            else if (head->AceType == ACCESS_DENIED_ACE_TYPE)
            {
                ::ACCESS_DENIED_ACE* ace =
                    static_cast< ::ACCESS_DENIED_ACE*>(ptr);

                std::cout
                    << "mask=0x"
                    << std::hex << ace->Mask << std::dec << '\n';

                ::SID* sid =
                    reinterpret_cast< ::SID*>(&(ace->SidStart));

                std::cout << "sid=" << to_string(sid) << '\n';

                if ((head->AceFlags & INHERIT_ONLY_ACE) == 0)
                {
                    denied |= to_permissions(
                        sid, info.owner(), info.group(), ace->Mask);
                }
            }
        }
        std::cout << "----------------------------------------\n";
        std::cout
            << "permissions=0" << std::oct << std::setfill('0')
            << std::setw(3) << (allowed & ~denied) << std::dec << std::endl;

        return 0;
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    return 1;
}