X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=blobdiff_plain;f=source%2Ffs%2Fstat.cpp;h=85fead5e7de1327eac1ade4c2e511d199badd8c8;hp=ccd344b875e809ecdbf3016e96d4576ab0c898dc;hb=df74f89b175c1579f9383e6dec8ddb821e606642;hpb=19edaf3b45bc4c23459fc8ddd552dcfa33eecc71 diff --git a/source/fs/stat.cpp b/source/fs/stat.cpp index ccd344b..85fead5 100644 --- a/source/fs/stat.cpp +++ b/source/fs/stat.cpp @@ -1,74 +1,307 @@ #ifdef WIN32 -#include +#include +#include +#else +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include #endif #include +#include +#include #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(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