From e240e074ce15c17d644e378067c2941aefcd5611 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 30 Jul 2011 11:18:35 +0300 Subject: [PATCH] Rewrite the stat functions to expose a platform-independent interface --- source/fs/dir.cpp | 11 +-- source/fs/stat.cpp | 169 +++++++++++++++++++++++++++++++++----------- source/fs/stat.h | 73 +++++++++++++++---- source/fs/utils.cpp | 3 +- 4 files changed, 190 insertions(+), 66 deletions(-) diff --git a/source/fs/dir.cpp b/source/fs/dir.cpp index c53c156..75ad8d5 100644 --- a/source/fs/dir.cpp +++ b/source/fs/dir.cpp @@ -85,16 +85,12 @@ void mkpath(const Path &path, int mode) if(p.size()==1 && p.is_absolute()) continue; #endif - struct stat st; - int err = stat(p, st); - if(err==0) + if(exists(p)) { - if(!S_ISDIR(st.st_mode)) + if(!is_dir(p)) throw not_a_directory(p); continue; } - else if(errno!=ENOENT) - throw system_error("mkpath:stat"); else mkdir(p, mode); } @@ -112,8 +108,7 @@ void rmdirs(const Path &path) for(list::iterator i=files.begin(); i!=files.end(); ++i) { Path p = path / *i; - struct stat st = stat(p.str().c_str()); - if(S_ISDIR(st.st_mode)) + if(is_dir(p)) rmdirs(p); else unlink(p); diff --git a/source/fs/stat.cpp b/source/fs/stat.cpp index ccd344b..245dd4e 100644 --- a/source/fs/stat.cpp +++ b/source/fs/stat.cpp @@ -1,74 +1,161 @@ #ifdef WIN32 -#include +#include +#include +#else +#include +#include +#include #endif #include +#include +#include #include "path.h" #include "stat.h" -namespace Msp { -namespace FS { +using namespace std; -int stat(const Path &fn, struct stat &st) -{ - return ::stat(fn.str().c_str(), &st); -} +namespace { -struct stat stat(const Path &fn) +#ifdef WIN32 +string get_account_name(PSID sid) { - struct stat st; - if(stat(fn, st)==-1) - throw system_error("stat"); - return st; + 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); } - -int lstat(const Path &fn, struct stat &st) -{ -#ifdef WIN32 - return stat(fn, st); -#else - return ::lstat(fn.str().c_str(), &st); #endif + } -struct stat lstat(const Path &fn) + +namespace Msp { +namespace FS { + +#ifndef WIN32 +struct Stat::Private { - struct stat st; - if(lstat(fn, st)==-1) - throw system_error("lstat"); - return st; -} + static Stat from_struct_stat(const struct stat &); +}; -bool exists(const Path &path) +Stat Stat::Private::from_struct_stat(const struct stat &st) { - return access(path.str().c_str(), F_OK)==0; + Stat result; + 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); + + char buf[1024]; + + struct passwd pw; + struct passwd *owner; + if(!getpwuid_r(st.st_uid, &pw, buf, sizeof(buf), &owner) && owner) + result.owner = owner->pw_name; + else + result.owner = format("%d", st.st_uid); + + struct group gr; + struct group *group; + if(!getgrgid_r(st.st_gid, &gr, buf, sizeof(buf), &group) && group) + result.group = group->gr_name; + else + result.group = format("%d", st.st_gid); + + return result; } +#endif + +Stat::Stat(): + type(UNKNOWN), + size(0), + alloc_size(0) +{ } -bool is_reg(const Path &path) +Stat Stat::stat(const Path &path) { +#ifdef WIN32 + HANDLE handle; + handle = CreateFile(path.str().c_str(), 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(handle==INVALID_HANDLE_VALUE) + throw system_error("CreateFile"); + + 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; + PSID group; + if(!GetSecurityInfo(handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION, &owner, &group, 0, 0, &sec_desc)) + { + CloseHandle(handle); + throw system_error("GetSecurityInfo"); + } + + result.owner = get_account_name(owner); + result.group = get_account_name(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) + throw system_error("stat"); + + return Private::from_struct_stat(st); +#endif } -bool is_dir(const Path &path) +Stat Stat::lstat(const Path &path) { +#ifdef WIN32 + return stat(path); +#else struct stat st; - if(stat(path, st)==0) - return S_ISDIR(st.st_mode); - return false; + int ret = ::lstat(path.str().c_str(), &st); + if(ret==-1) + throw system_error("lstat"); + + return Private::from_struct_stat(st); +#endif } -bool is_link(const Path &path) +bool exists(const Path &path) { #ifdef WIN32 - (void)path; + return GetFileAttributes(path.str().c_str())!=INVALID_FILE_ATTRIBUTES; #else - struct stat st; - if(lstat(path, st)==0) - return S_ISLNK(st.st_mode); + return access(path.str().c_str(), F_OK)==0; #endif - return false; } } // namespace FS diff --git a/source/fs/stat.h b/source/fs/stat.h index 9056c92..0d5f6de 100644 --- a/source/fs/stat.h +++ b/source/fs/stat.h @@ -1,38 +1,81 @@ #ifndef MSP_FS_STAT_H_ #define MSP_FS_STAT_H_ -#include +#include +#include #include "path.h" namespace Msp { namespace FS { -/** Gets information about a file. Returns 0 on success or -1 on error. This -version can be used to check for file existence and get information in one -call. */ -int stat(const Path &fn, struct stat &st); +enum FileType +{ + UNKNOWN, + REGULAR, + DIRECTORY, + SYMLINK +}; -/** Returns information about a file. This version throws an exception if an -error occurs. */ -struct stat stat(const Path &fn); +#ifdef MSVC +typedef __uint64 FileSize; +#else +typedef unsigned long long FileSize; +#endif + +/** +Holds file information. +*/ +class Stat +{ +private: + struct Private; + + FileType type; + FileSize size; + FileSize alloc_size; + Time::TimeStamp mtime; + std::string owner; + std::string group; + + Stat(); +public: + FileType get_type() const { return type; } + bool is_regular() const { return type==REGULAR; } + bool is_directory() const { return type==DIRECTORY; } + bool is_symlink() const { return type==SYMLINK; } + 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; } + + /// Returns a Stat object describing a file. + static Stat stat(const Path &); + static Stat lstat(const Path &); +}; -/// Gets information about a file, without following symbolic links -int lstat(const Path &fn, struct stat &st); +/// Convenience wrapper for Stat::stat +inline Stat stat(const Path &path) +{ return Stat::stat(path); } -/// Returns information about a file, without following symbolic links -struct stat lstat(const Path &fn); +/// Convenience wrapper for Stat::lstat +inline Stat lstat(const Path &path) +{ return Stat::lstat(path); } /// Tests for existence of a file bool exists(const Path &path); /// Tests whether a path refers to an existing regular file -bool is_reg(const Path &path); +inline bool is_reg(const Path &path) +{ return stat(path).is_regular(); } /// Tests whether a path refers to an existing directory -bool is_dir(const Path &path); +inline bool is_dir(const Path &path) +{ return stat(path).is_directory(); } /// Tests whether a path refers to a symbolic link -bool is_link(const Path &path); +inline bool is_link(const Path &path) +{ return lstat(path).is_symlink(); } } // namespace FS } // namespace Msp diff --git a/source/fs/utils.cpp b/source/fs/utils.cpp index e3c09fd..b66b82d 100644 --- a/source/fs/utils.cpp +++ b/source/fs/utils.cpp @@ -109,8 +109,7 @@ Path realpath(const Path &path) Path next = real/queue.front(); queue.pop_front(); - struct stat st = lstat(next); - if(S_ISLNK(st.st_mode)) + if(is_link(next)) { if(++n_links>64) throw runtime_error("too many symbolic links"); -- 2.43.0