]> git.tdb.fi Git - libs/core.git/blob - source/fs/windows/stat.cpp
Check errors from CreateSemaphore
[libs/core.git] / source / fs / windows / stat.cpp
1 #define MSP_FULL_WINAPI
2 #include <msp/core/winapi.h>
3 #include <aclapi.h>
4 #include <msp/core/systemerror.h>
5 #include <msp/strings/format.h>
6 #include <msp/time/rawtime_private.h>
7 #include "stat.h"
8 #include "stat_private.h"
9
10 using namespace std;
11
12 namespace {
13
14 PSID copy_sid(PSID sid)
15 {
16         if(!sid || !IsValidSid(sid))
17                 return nullptr;
18         DWORD len = GetLengthSid(sid);
19         PSID copy = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len));
20         if(!CopySid(len, copy, sid))
21         {
22                 DWORD err = GetLastError();
23                 HeapFree(GetProcessHeap(), 0, copy);
24                 throw Msp::system_error("CopySid", err);
25         }
26         return copy;
27 }
28
29 string get_account_name(PSID sid)
30 {
31         char name[1024];
32         DWORD nlen = sizeof(name);
33         char domain[1024];
34         DWORD dlen = sizeof(domain);
35         SID_NAME_USE use;
36         if(!LookupAccountSid(nullptr, sid, name, &nlen, domain, &dlen, &use))
37                 throw Msp::system_error("LookupAccountSid");
38         return Msp::format("%s/%s", name, domain);
39 }
40
41 DWORD get_reparse_point_tag(HANDLE handle)
42 {
43         char data[1024];
44         DWORD size;
45         if(DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, data, sizeof(data), &size, nullptr))
46                 return *reinterpret_cast<DWORD *>(data);
47         else
48                 return 0;
49 }
50
51 }
52
53
54 namespace Msp {
55 namespace FS {
56
57 Stat::Private::Private(const Private &other):
58         owner_id(copy_sid(other.owner_id)),
59         group_id(copy_sid(other.group_id))
60 { }
61
62 Stat::Private::~Private()
63 {
64         if(owner_id!=INVALID_UID)
65                 HeapFree(GetProcessHeap(), 0, owner_id);
66         if(group_id!=INVALID_GID)
67                 HeapFree(GetProcessHeap(), 0, group_id);
68 }
69
70 Stat Stat::Private::sys_stat(const Path &path, bool follow_symlinks)
71 {
72         HANDLE handle;
73         DWORD reparse_tag = 0;
74         DWORD flags = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS;
75         if(!follow_symlinks)
76                 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
77         handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, flags, nullptr);
78         if(handle==INVALID_HANDLE_VALUE)
79         {
80                 DWORD err = GetLastError();
81                 if(err==ERROR_FILE_NOT_FOUND || err==ERROR_PATH_NOT_FOUND)
82                         return Stat();
83                 else if(err==ERROR_CANT_ACCESS_FILE && follow_symlinks)
84                 {
85                         flags |= FILE_FLAG_OPEN_REPARSE_POINT;
86                         handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, flags, nullptr);
87                         if(handle!=INVALID_HANDLE_VALUE)
88                         {
89                                 reparse_tag = get_reparse_point_tag(handle);
90                                 /* App exec links are used for apps installed from the Microsoft
91                                 Store, but the apps themselves are stored in a location regular
92                                 users aren't permitted to access.  Get information for the reparse
93                                 point itself even if following symlinks was requested. */
94                                 if(reparse_tag!=IO_REPARSE_TAG_APPEXECLINK)
95                                 {
96                                         CloseHandle(handle);
97                                         handle = INVALID_HANDLE_VALUE;
98                                 }
99                         }
100                 }
101
102                 if(handle==INVALID_HANDLE_VALUE)
103                         throw system_error(format("CreateFile(%s)", path), err);
104         }
105
106         BY_HANDLE_FILE_INFORMATION info;
107         if(!GetFileInformationByHandle(handle, &info))
108         {
109                 CloseHandle(handle);
110                 throw system_error("GetFileInformationByHandle");
111         }
112
113         Stat result;
114         result.exists = true;
115         if(info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
116                 result.type = DIRECTORY;
117         else
118         {
119                 if((info.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) && !reparse_tag)
120                         reparse_tag = get_reparse_point_tag(handle);
121                 if(reparse_tag==IO_REPARSE_TAG_SYMLINK)
122                         result.type = SYMLINK;
123                 else
124                         result.type = REGULAR;
125         }
126
127         result.size = FileSize(info.nFileSizeHigh)<<32 | info.nFileSizeLow;
128         result.alloc_size = (result.size+511)&~511;
129         result.mtime = Time::TimeStamp(Time::filetime_to_rawtime(info.ftLastWriteTime));
130
131         PSECURITY_DESCRIPTOR sec_desc;
132         PSID owner = nullptr;
133         PSID group = nullptr;
134         const SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION;
135         DWORD err = GetSecurityInfo(handle, SE_FILE_OBJECT, sec_info, &owner, &group, nullptr, nullptr, &sec_desc);
136         if(err)
137         {
138                 CloseHandle(handle);
139                 throw system_error("GetSecurityInfo", err);
140         }
141
142         result.priv = new Private;
143         result.priv->owner_id = copy_sid(owner);
144         result.priv->group_id = copy_sid(group);
145
146         LocalFree(sec_desc);
147
148         CloseHandle(handle);
149
150         return result;
151 }
152
153 void Stat::Private::fill_owner_info(Stat::OwnerInfo &result)
154 {
155         if(owner_id!=INVALID_UID)
156                 result.owner = get_account_name(owner_id);
157         else
158                 result.owner = "None";
159
160         if(group_id!=INVALID_GID)
161                 result.group = get_account_name(group_id);
162         else
163                 result.group = "None";
164 }
165
166
167 Stat Stat::stat(const Path &path)
168 {
169         return Private::sys_stat(path, true);
170 }
171
172 Stat Stat::lstat(const Path &path)
173 {
174         return Private::sys_stat(path, false);
175 }
176
177 bool exists(const Path &path)
178 {
179         return GetFileAttributes(path.str().c_str())!=INVALID_FILE_ATTRIBUTES;
180 }
181
182 } // namespace FS
183 } // namespace Msp