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