X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=blobdiff_plain;f=source%2Ffs%2Fstat.cpp;h=85fead5e7de1327eac1ade4c2e511d199badd8c8;hp=2419c2990c9bd9353f06e1ddcaf481afae6bb03e;hb=df74f89b175c1579f9383e6dec8ddb821e606642;hpb=efdd4ab5f33b444d3549543cbaf5372241c9cf73 diff --git a/source/fs/stat.cpp b/source/fs/stat.cpp index 2419c29..85fead5 100644 --- a/source/fs/stat.cpp +++ b/source/fs/stat.cpp @@ -3,6 +3,8 @@ #include #else #define _FILE_OFFSET_BITS 64 +#include +#include #include #include #include @@ -18,6 +20,21 @@ using namespace std; namespace { #ifdef WIN32 +PSID copy_sid(PSID sid) +{ + if(!sid || !IsValidSid(sid)) + return 0; + DWORD len = GetLengthSid(sid); + PSID copy = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len)); + if(!CopySid(len, copy, sid)) + { + DWORD err = GetLastError(); + HeapFree(GetProcessHeap(), 0, copy); + throw Msp::system_error("CopySid", err); + } + return copy; +} + string get_account_name(PSID sid) { char name[1024]; @@ -37,15 +54,59 @@ string get_account_name(PSID sid) namespace Msp { namespace FS { -#ifndef WIN32 struct Stat::Private { +#ifdef WIN32 + PSID owner_id; + PSID group_id; +#else + uid_t owner_id; + gid_t group_id; +#endif + + Private(); + Private(const Private &); + ~Private(); + +#ifndef WIN32 + /* This is here because it needs access to private members of Stat, but we + can't expose the system stat struct in the public header */ static Stat from_struct_stat(const struct stat &); +#endif + + void fill_owner_info(Stat::OwnerInfo &); }; +Stat::Private::Private(): + owner_id(0), + group_id(0) +{ } + +Stat::Private::Private(const Private &other): +#ifdef WIN32 + owner_id(copy_sid(other.owner_id)), + group_id(copy_sid(other.group_id)) +#else + owner_id(other.owner_id), + group_id(other.group_id) +#endif +{ } + +Stat::Private::~Private() +{ +#ifdef WIN32 + if(owner_id) + HeapFree(GetProcessHeap(), 0, owner_id); + if(group_id) + HeapFree(GetProcessHeap(), 0, group_id); +#endif +} + +#ifndef WIN32 Stat Stat::Private::from_struct_stat(const struct stat &st) { Stat result; + result.exists = true; if(S_ISREG(st.st_mode)) result.type = REGULAR; else if(S_ISDIR(st.st_mode)) @@ -58,39 +119,96 @@ Stat Stat::Private::from_struct_stat(const struct stat &st) result.alloc_size = st.st_blocks*512; result.mtime = Time::TimeStamp::from_unixtime(st.st_mtime); + result.priv = new Private; + result.priv->owner_id = st.st_uid; + result.priv->group_id = st.st_gid; + + return result; +} +#endif + +void Stat::Private::fill_owner_info(Stat::OwnerInfo &result) +{ +#ifdef WIN32 + if(owner_id) + result.owner = get_account_name(owner_id); + else + result.owner = "None"; + + if(group_id) + result.group = get_account_name(group_id); + else + result.group = "None"; +#else char buf[1024]; struct passwd pw; struct passwd *owner; - if(!getpwuid_r(st.st_uid, &pw, buf, sizeof(buf), &owner) && owner) + if(!getpwuid_r(owner_id, &pw, buf, sizeof(buf), &owner) && owner) result.owner = owner->pw_name; else - result.owner = format("%d", st.st_uid); + result.owner = format("%d", owner_id); struct group gr; struct group *group; - if(!getgrgid_r(st.st_gid, &gr, buf, sizeof(buf), &group) && group) + if(!getgrgid_r(group_id, &gr, buf, sizeof(buf), &group) && group) result.group = group->gr_name; else - result.group = format("%d", st.st_gid); - - return result; -} + result.group = format("%d", group_id); #endif +} + Stat::Stat(): + exists(false), type(UNKNOWN), size(0), - alloc_size(0) + alloc_size(0), + priv(0) +{ } + +Stat::Stat(const Stat &other): + exists(other.exists), + type(other.type), + size(other.type), + alloc_size(other.alloc_size), + mtime(other.mtime), + owner_info(other.owner_info), + priv(other.priv ? new Private(*other.priv) : 0) { } +Stat &Stat::operator=(const Stat &other) +{ + exists = other.exists; + type = other.type; + size = other.size; + alloc_size = other.alloc_size; + mtime = other.mtime; + owner_info = other.owner_info; + delete priv; + priv = (other.priv ? new Private(*other.priv) : 0); + + return *this; +} + +Stat::~Stat() +{ + delete priv; +} + Stat Stat::stat(const Path &path) { #ifdef WIN32 HANDLE handle; - handle = CreateFile(path.str().c_str(), 0, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 0); if(handle==INVALID_HANDLE_VALUE) - throw system_error("CreateFile"); + { + DWORD err = GetLastError(); + if(err==ERROR_FILE_NOT_FOUND) + return Stat(); + else + throw system_error("CreateFile", err); + } BY_HANDLE_FILE_INFORMATION info; if(!GetFileInformationByHandle(handle, &info)) @@ -110,16 +228,19 @@ Stat Stat::stat(const Path &path) result.mtime = Time::TimeStamp(Time::filetime_to_rawtime(info.ftLastWriteTime)); PSECURITY_DESCRIPTOR sec_desc; - PSID owner; - PSID group; - if(!GetSecurityInfo(handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION, &owner, &group, 0, 0, &sec_desc)) + PSID owner = 0; + PSID group = 0; + const SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION; + DWORD err = GetSecurityInfo(handle, SE_FILE_OBJECT, sec_info, &owner, &group, 0, 0, &sec_desc); + if(err) { CloseHandle(handle); - throw system_error("GetSecurityInfo"); + throw system_error("GetSecurityInfo", err); } - result.owner = get_account_name(owner); - result.group = get_account_name(group); + result.priv = new Private; + result.priv->owner_id = copy_sid(owner); + result.priv->group_id = copy_sid(group); LocalFree(sec_desc); @@ -130,12 +251,31 @@ Stat Stat::stat(const Path &path) struct stat st; int ret = ::stat(path.str().c_str(), &st); if(ret==-1) - throw system_error("stat"); + { + if(errno==ENOENT) + return Stat(); + else + throw system_error("stat"); + } return Private::from_struct_stat(st); #endif } +const std::string &Stat::get_owner() const +{ + if(priv && owner_info.owner.empty()) + priv->fill_owner_info(owner_info); + return owner_info.owner; +} + +const std::string &Stat::get_group() const +{ + if(priv && owner_info.group.empty()) + priv->fill_owner_info(owner_info); + return owner_info.group; +} + Stat Stat::lstat(const Path &path) { #ifdef WIN32 @@ -144,7 +284,12 @@ Stat Stat::lstat(const Path &path) struct stat st; int ret = ::lstat(path.str().c_str(), &st); if(ret==-1) - throw system_error("lstat"); + { + if(errno==ENOENT) + return Stat(); + else + throw system_error("lstat"); + } return Private::from_struct_stat(st); #endif