Fix GetSecurityInfo error checking and request appropriate access to the file
[libs/core.git] / source / fs / stat.cpp
1 #ifdef WIN32
2 #include <windows.h>
3 #include <aclapi.h>
4 #else
5 #define _FILE_OFFSET_BITS 64
6 #include <cerrno>
7 #include <unistd.h>
8 #include <sys/stat.h>
9 #include <grp.h>
10 #include <pwd.h>
11 #endif
12 #include <msp/core/systemerror.h>
13 #include <msp/strings/format.h>
14 #include <msp/time/rawtime_private.h>
15 #include "path.h"
16 #include "stat.h"
17
18 using namespace std;
19
20 namespace {
21
22 #ifdef WIN32
23 string get_account_name(PSID sid)
24 {
25         char name[1024];
26         DWORD nlen = sizeof(name);
27         char domain[1024];
28         DWORD dlen = sizeof(domain);
29         SID_NAME_USE use;
30         if(!LookupAccountSid(0, sid, name, &nlen, domain, &dlen, &use))
31                 throw Msp::system_error("LookupAccountSid");
32         return Msp::format("%s/%s", name, domain);
33 }
34 #endif
35
36 }
37
38
39 namespace Msp {
40 namespace FS {
41
42 #ifndef WIN32
43 struct Stat::Private
44 {
45         static Stat from_struct_stat(const struct stat &);
46 };
47
48 Stat Stat::Private::from_struct_stat(const struct stat &st)
49 {
50         Stat result;
51         result.exists = true;
52         if(S_ISREG(st.st_mode))
53                 result.type = REGULAR;
54         else if(S_ISDIR(st.st_mode))
55                 result.type = DIRECTORY;
56         else if(S_ISLNK(st.st_mode))
57                 result.type = SYMLINK;
58         else
59                 result.type = UNKNOWN;
60         result.size = st.st_size;
61         result.alloc_size = st.st_blocks*512;
62         result.mtime = Time::TimeStamp::from_unixtime(st.st_mtime);
63
64         char buf[1024];
65
66         struct passwd pw;
67         struct passwd *owner;
68         if(!getpwuid_r(st.st_uid, &pw, buf, sizeof(buf), &owner) && owner)
69                 result.owner = owner->pw_name;
70         else
71                 result.owner = format("%d", st.st_uid);
72
73         struct group gr;
74         struct group *group;
75         if(!getgrgid_r(st.st_gid, &gr, buf, sizeof(buf), &group) && group)
76                 result.group = group->gr_name;
77         else
78                 result.group = format("%d", st.st_gid);
79
80         return result;
81 }
82 #endif
83
84 Stat::Stat():
85         exists(false),
86         type(UNKNOWN),
87         size(0),
88         alloc_size(0)
89 { }
90
91 Stat Stat::stat(const Path &path)
92 {
93 #ifdef WIN32
94         HANDLE handle;
95         handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 0);
96         if(handle==INVALID_HANDLE_VALUE)
97         {
98                 DWORD err = GetLastError();
99                 if(err==ERROR_FILE_NOT_FOUND)
100                         return Stat();
101                 else
102                         throw system_error("CreateFile", err);
103         }
104
105         BY_HANDLE_FILE_INFORMATION info;
106         if(!GetFileInformationByHandle(handle, &info))
107         {
108                 CloseHandle(handle);
109                 throw system_error("GetFileInformationByHandle");
110         }
111
112         Stat result;
113         if(info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
114                 result.type = DIRECTORY;
115         else
116                 result.type = REGULAR;
117
118         result.size = FileSize(info.nFileSizeHigh)<<32 | info.nFileSizeLow;
119         result.alloc_size = (result.size+511)&~511;
120         result.mtime = Time::TimeStamp(Time::filetime_to_rawtime(info.ftLastWriteTime));
121
122         PSECURITY_DESCRIPTOR sec_desc;
123         PSID owner = 0;
124         PSID group = 0;
125         const SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION;
126         DWORD err = GetSecurityInfo(handle, SE_FILE_OBJECT, sec_info, &owner, &group, 0, 0, &sec_desc);
127         if(err)
128         {
129                 CloseHandle(handle);
130                 throw system_error("GetSecurityInfo", err);
131         }
132
133         if(owner)
134                 result.owner = get_account_name(owner);
135         if(group)
136                 result.group = get_account_name(group);
137
138         LocalFree(sec_desc);
139
140         CloseHandle(handle);
141
142         return result;
143 #else
144         struct stat st;
145         int ret = ::stat(path.str().c_str(), &st);
146         if(ret==-1)
147         {
148                 if(errno==ENOENT)
149                         return Stat();
150                 else
151                         throw system_error("stat");
152         }
153
154         return Private::from_struct_stat(st);
155 #endif
156 }
157
158 Stat Stat::lstat(const Path &path)
159 {
160 #ifdef WIN32
161         return stat(path);
162 #else
163         struct stat st;
164         int ret = ::lstat(path.str().c_str(), &st);
165         if(ret==-1)
166         {
167                 if(errno==ENOENT)
168                         return Stat();
169                 else
170                         throw system_error("lstat");
171         }
172
173         return Private::from_struct_stat(st);
174 #endif
175 }
176
177 bool exists(const Path &path)
178 {
179 #ifdef WIN32
180         return GetFileAttributes(path.str().c_str())!=INVALID_FILE_ATTRIBUTES;
181 #else
182         return access(path.str().c_str(), F_OK)==0;
183 #endif
184 }
185
186 } // namespace FS
187 } // namespace Msp