1 #define MSP_FULL_WINAPI
2 #include <msp/core/winapi.h>
4 #include <msp/core/systemerror.h>
5 #include <msp/strings/format.h>
6 #include <msp/time/rawtime_private.h>
8 #include "stat_private.h"
14 PSID copy_sid(PSID sid)
16 if(!sid || !IsValidSid(sid))
18 DWORD len = GetLengthSid(sid);
19 PSID copy = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len));
20 if(!CopySid(len, copy, sid))
22 DWORD err = GetLastError();
23 HeapFree(GetProcessHeap(), 0, copy);
24 throw Msp::system_error("CopySid", err);
29 string get_account_name(PSID sid)
32 DWORD nlen = sizeof(name);
34 DWORD dlen = sizeof(domain);
36 if(!LookupAccountSid(nullptr, sid, name, &nlen, domain, &dlen, &use))
37 throw Msp::system_error("LookupAccountSid");
38 return Msp::format("%s/%s", name, domain);
41 DWORD get_reparse_point_tag(HANDLE handle)
45 if(DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, data, sizeof(data), &size, nullptr))
46 return *reinterpret_cast<DWORD *>(data);
57 Stat::Private::Private(const Private &other):
58 owner_id(copy_sid(other.owner_id)),
59 group_id(copy_sid(other.group_id))
62 Stat::Private::~Private()
64 if(owner_id!=INVALID_UID)
65 HeapFree(GetProcessHeap(), 0, owner_id);
66 if(group_id!=INVALID_GID)
67 HeapFree(GetProcessHeap(), 0, group_id);
70 Stat Stat::Private::sys_stat(const Path &path, bool follow_symlinks)
73 DWORD reparse_tag = 0;
74 DWORD flags = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS;
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)
80 DWORD err = GetLastError();
81 if(err==ERROR_FILE_NOT_FOUND || err==ERROR_PATH_NOT_FOUND)
83 else if(err==ERROR_CANT_ACCESS_FILE && follow_symlinks)
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)
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)
97 handle = INVALID_HANDLE_VALUE;
102 if(handle==INVALID_HANDLE_VALUE)
103 throw system_error(format("CreateFile(%s)", path), err);
106 BY_HANDLE_FILE_INFORMATION info;
107 if(!GetFileInformationByHandle(handle, &info))
110 throw system_error("GetFileInformationByHandle");
114 result.exists = true;
115 if(info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
116 result.type = DIRECTORY;
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;
124 result.type = REGULAR;
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));
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);
139 throw system_error("GetSecurityInfo", err);
142 result.priv = new Private;
143 result.priv->owner_id = copy_sid(owner);
144 result.priv->group_id = copy_sid(group);
153 void Stat::Private::fill_owner_info(Stat::OwnerInfo &result)
155 if(owner_id!=INVALID_UID)
156 result.owner = get_account_name(owner_id);
158 result.owner = "None";
160 if(group_id!=INVALID_GID)
161 result.group = get_account_name(group_id);
163 result.group = "None";
167 Stat Stat::stat(const Path &path)
169 return Private::sys_stat(path, true);
172 Stat Stat::lstat(const Path &path)
174 return Private::sys_stat(path, false);
177 bool exists(const Path &path)
179 return GetFileAttributes(path.str().c_str())!=INVALID_FILE_ATTRIBUTES;