]> git.tdb.fi Git - libs/core.git/commitdiff
Rewrite the stat functions to expose a platform-independent interface
authorMikko Rasa <tdb@tdb.fi>
Sat, 30 Jul 2011 08:18:35 +0000 (11:18 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 30 Jul 2011 08:39:19 +0000 (11:39 +0300)
source/fs/dir.cpp
source/fs/stat.cpp
source/fs/stat.h
source/fs/utils.cpp

index c53c156fec68b6e2df51c2afdb09396281ae92bd..75ad8d5ecd39e0144eb7cd2f0be4ee83ab0604e8 100644 (file)
@@ -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<string>::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);
index ccd344b875e809ecdbf3016e96d4576ab0c898dc..245dd4ea9e05e4510768343ad8a5ff62a066313f 100644 (file)
 #ifdef WIN32
-#include <io.h>
+#include <windows.h>
+#include <aclapi.h>
+#else
+#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"
 
-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
index 9056c926d697ed03ae04df82c62d7e2ec8661455..0d5f6de184b6f7a725e648119172573125799375 100644 (file)
@@ -1,38 +1,81 @@
 #ifndef MSP_FS_STAT_H_
 #define MSP_FS_STAT_H_
 
-#include <sys/stat.h>
+#include <string>
+#include <msp/time/timestamp.h>
 #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
index e3c09fde6e79aa9c3d446d060a377ebeb8771ba8..b66b82dd70c90c1ed96b6f0cd13a27087ad4ebf5 100644 (file)
@@ -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");