From: Mikko Rasa Date: Mon, 9 Jul 2012 16:18:48 +0000 (+0300) Subject: Defer the retrieval of owner/group names until they are needed X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=commitdiff_plain;h=df74f89b175c1579f9383e6dec8ddb821e606642 Defer the retrieval of owner/group names until they are needed --- diff --git a/source/fs/stat.cpp b/source/fs/stat.cpp index 77564f0..85fead5 100644 --- a/source/fs/stat.cpp +++ b/source/fs/stat.cpp @@ -20,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]; @@ -39,12 +54,55 @@ 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; @@ -61,33 +119,83 @@ 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 @@ -130,10 +238,9 @@ Stat Stat::stat(const Path &path) throw system_error("GetSecurityInfo", err); } - if(owner) - result.owner = get_account_name(owner); - if(group) - 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); @@ -155,6 +262,20 @@ Stat Stat::stat(const Path &path) #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 diff --git a/source/fs/stat.h b/source/fs/stat.h index 6ae551f..8c05771 100644 --- a/source/fs/stat.h +++ b/source/fs/stat.h @@ -30,16 +30,25 @@ class Stat private: struct Private; + struct OwnerInfo + { + std::string owner; + std::string group; + }; + bool exists; FileType type; FileSize size; FileSize alloc_size; Time::TimeStamp mtime; - std::string owner; - std::string group; + mutable OwnerInfo owner_info; + Private *priv; public: Stat(); + Stat(const Stat &); + Stat &operator=(const Stat &); + ~Stat(); FileType get_type() const { return type; } bool is_regular() const { return type==REGULAR; } @@ -48,8 +57,8 @@ public: FileSize get_size() const { return size; } FileSize get_alloc_size() const { return alloc_size; } const Time::TimeStamp &get_modify_time() const { return mtime; } - const std::string &get_owner() const { return owner; } - const std::string &get_group() const { return group; } + const std::string &get_owner() const; + const std::string &get_group() const; operator bool() const { return exists; }