#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
#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