Defer the retrieval of owner/group names until they are needed
authorMikko Rasa <tdb@tdb.fi>
Mon, 9 Jul 2012 16:18:48 +0000 (19:18 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 9 Jul 2012 16:18:48 +0000 (19:18 +0300)
source/fs/stat.cpp
source/fs/stat.h

index 77564f0ecd2d6c49b3297db2733686423de870bb..85fead5e7de1327eac1ade4c2e511d199badd8c8 100644 (file)
@@ -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<PSID>(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
index 6ae551fd13836955bbd6a11f347a2b236abe0022..8c05771ebe9b85bd27b42fb4e674a4b1a1f5d2b4 100644 (file)
@@ -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; }