]> git.tdb.fi Git - libs/core.git/blobdiff - source/fs/stat.cpp
Defer the retrieval of owner/group names until they are needed
[libs/core.git] / source / fs / stat.cpp
index ccd344b875e809ecdbf3016e96d4576ab0c898dc..85fead5e7de1327eac1ade4c2e511d199badd8c8 100644 (file)
 #ifdef WIN32
-#include <io.h>
+#include <windows.h>
+#include <aclapi.h>
+#else
+#define _FILE_OFFSET_BITS 64
+#include <cerrno>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <pwd.h>
 #endif
 #include <msp/core/systemerror.h>
+#include <msp/strings/format.h>
+#include <msp/time/rawtime_private.h>
 #include "path.h"
 #include "stat.h"
 
+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];
+       DWORD nlen = sizeof(name);
+       char domain[1024];
+       DWORD dlen = sizeof(domain);
+       SID_NAME_USE use;
+       if(!LookupAccountSid(0, sid, name, &nlen, domain, &dlen, &use))
+               throw Msp::system_error("LookupAccountSid");
+       return Msp::format("%s/%s", name, domain);
+}
+#endif
+
+}
+
+
 namespace Msp {
 namespace FS {
 
-int stat(const Path &fn, struct stat &st)
+struct Stat::Private
 {
-       return ::stat(fn.str().c_str(), &st);
+#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
 }
 
-struct stat stat(const Path &fn)
+#ifndef WIN32
+Stat Stat::Private::from_struct_stat(const struct stat &st)
 {
-       struct stat st;
-       if(stat(fn, st)==-1)
-               throw system_error("stat");
-       return st;
+       Stat result;
+       result.exists = true;
+       if(S_ISREG(st.st_mode))
+               result.type = REGULAR;
+       else if(S_ISDIR(st.st_mode))
+               result.type = DIRECTORY;
+       else if(S_ISLNK(st.st_mode))
+               result.type = SYMLINK;
+       else
+               result.type = UNKNOWN;
+       result.size = st.st_size;
+       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
 
-int lstat(const Path &fn, struct stat &st)
+void Stat::Private::fill_owner_info(Stat::OwnerInfo &result)
 {
 #ifdef WIN32
-       return stat(fn, st);
+       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
-       return ::lstat(fn.str().c_str(), &st);
+       char buf[1024];
+
+       struct passwd pw;
+       struct passwd *owner;
+       if(!getpwuid_r(owner_id, &pw, buf, sizeof(buf), &owner) && owner)
+               result.owner = owner->pw_name;
+       else
+               result.owner = format("%d", owner_id);
+
+       struct group gr;
+       struct group *group;
+       if(!getgrgid_r(group_id, &gr, buf, sizeof(buf), &group) && group)
+               result.group = group->gr_name;
+       else
+               result.group = format("%d", group_id);
 #endif
 }
 
-struct stat lstat(const Path &fn)
+
+Stat::Stat():
+       exists(false),
+       type(UNKNOWN),
+       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)
 {
-       struct stat st;
-       if(lstat(fn, st)==-1)
-               throw system_error("lstat");
-       return st;
+       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;
 }
 
-bool exists(const Path &path)
+Stat::~Stat()
 {
-       return access(path.str().c_str(), F_OK)==0;
+       delete priv;
 }
 
-bool is_reg(const Path &path)
+Stat Stat::stat(const Path &path)
 {
+#ifdef WIN32
+       HANDLE handle;
+       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)
+       {
+               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))
+       {
+               CloseHandle(handle);
+               throw system_error("GetFileInformationByHandle");
+       }
+
+       Stat result;
+       if(info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+               result.type = DIRECTORY;
+       else
+               result.type = REGULAR;
+
+       result.size = FileSize(info.nFileSizeHigh)<<32 | info.nFileSizeLow;
+       result.alloc_size = (result.size+511)&~511;
+       result.mtime = Time::TimeStamp(Time::filetime_to_rawtime(info.ftLastWriteTime));
+
+       PSECURITY_DESCRIPTOR 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", err);
+       }
+
+       result.priv = new Private;
+       result.priv->owner_id = copy_sid(owner);
+       result.priv->group_id = copy_sid(group);
+
+       LocalFree(sec_desc);
+
+       CloseHandle(handle);
+
+       return result;
+#else
        struct stat st;
-       if(stat(path, st)==0)
-               return S_ISREG(st.st_mode);
-       return false;
+       int ret = ::stat(path.str().c_str(), &st);
+       if(ret==-1)
+       {
+               if(errno==ENOENT)
+                       return Stat();
+               else
+                       throw system_error("stat");
+       }
+
+       return Private::from_struct_stat(st);
+#endif
 }
 
-bool is_dir(const Path &path)
+const std::string &Stat::get_owner() const
 {
-       struct stat st;
-       if(stat(path, st)==0)
-               return S_ISDIR(st.st_mode);
-       return false;
+       if(priv && owner_info.owner.empty())
+               priv->fill_owner_info(owner_info);
+       return owner_info.owner;
 }
 
-bool is_link(const Path &path)
+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
-       (void)path;
+       return stat(path);
 #else
        struct stat st;
-       if(lstat(path, st)==0)
-               return S_ISLNK(st.st_mode);
+       int ret = ::lstat(path.str().c_str(), &st);
+       if(ret==-1)
+       {
+               if(errno==ENOENT)
+                       return Stat();
+               else
+                       throw system_error("lstat");
+       }
+
+       return Private::from_struct_stat(st);
+#endif
+}
+
+bool exists(const Path &path)
+{
+#ifdef WIN32
+       return GetFileAttributes(path.str().c_str())!=INVALID_FILE_ATTRIBUTES;
+#else
+       return access(path.str().c_str(), F_OK)==0;
 #endif
-       return false;
 }
 
 } // namespace FS