+#define MSP_FULL_WINAPI
#include <msp/core/winapi.h>
#include <aclapi.h>
#include <msp/core/systemerror.h>
return Msp::format("%s/%s", name, domain);
}
+DWORD get_reparse_point_tag(HANDLE handle)
+{
+ char data[1024];
+ DWORD size;
+ if(DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, data, sizeof(data), &size, nullptr))
+ return *reinterpret_cast<DWORD *>(data);
+ else
+ return 0;
+}
+
}
HeapFree(GetProcessHeap(), 0, group_id);
}
-void Stat::Private::fill_owner_info(Stat::OwnerInfo &result)
-{
- if(owner_id!=INVALID_UID)
- result.owner = get_account_name(owner_id);
- else
- result.owner = "None";
-
- if(group_id!=INVALID_GID)
- result.group = get_account_name(group_id);
- else
- result.group = "None";
-}
-
-
-Stat Stat::stat(const Path &path)
+Stat Stat::Private::sys_stat(const Path &path, bool follow_symlinks)
{
HANDLE handle;
- handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ DWORD reparse_tag = 0;
+ DWORD flags = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS;
+ if(!follow_symlinks)
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, flags, nullptr);
if(handle==INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if(err==ERROR_FILE_NOT_FOUND || err==ERROR_PATH_NOT_FOUND)
return Stat();
- else
+ else if(err==ERROR_CANT_ACCESS_FILE && follow_symlinks)
+ {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ handle = CreateFile(path.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, flags, nullptr);
+ if(handle!=INVALID_HANDLE_VALUE)
+ {
+ reparse_tag = get_reparse_point_tag(handle);
+ /* App exec links are used for apps installed from the Microsoft
+ Store, but the apps themselves are stored in a location regular
+ users aren't permitted to access. Get information for the reparse
+ point itself even if following symlinks was requested. */
+ if(reparse_tag!=IO_REPARSE_TAG_APPEXECLINK)
+ {
+ CloseHandle(handle);
+ handle = INVALID_HANDLE_VALUE;
+ }
+ }
+ }
+
+ if(handle==INVALID_HANDLE_VALUE)
throw system_error(format("CreateFile(%s)", path), err);
}
if(info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
result.type = DIRECTORY;
else
- result.type = REGULAR;
+ {
+ if((info.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) && !reparse_tag)
+ reparse_tag = get_reparse_point_tag(handle);
+ if(reparse_tag==IO_REPARSE_TAG_SYMLINK)
+ result.type = SYMLINK;
+ else
+ result.type = REGULAR;
+ }
result.size = FileSize(info.nFileSizeHigh)<<32 | info.nFileSizeLow;
result.alloc_size = (result.size+511)&~511;
return result;
}
+void Stat::Private::fill_owner_info(Stat::OwnerInfo &result)
+{
+ if(owner_id!=INVALID_UID)
+ result.owner = get_account_name(owner_id);
+ else
+ result.owner = "None";
+
+ if(group_id!=INVALID_GID)
+ result.group = get_account_name(group_id);
+ else
+ result.group = "None";
+}
+
+
+Stat Stat::stat(const Path &path)
+{
+ return Private::sys_stat(path, true);
+}
+
Stat Stat::lstat(const Path &path)
{
- return stat(path);
+ return Private::sys_stat(path, false);
}
bool exists(const Path &path)
+#define MSP_FULL_WINAPI
#include <msp/core/winapi.h>
#include <msp/core/except.h>
#include <msp/core/systemerror.h>
+#include <msp/stringcodec/utf16.h>
+#include <msp/stringcodec/utf8.h>
#include "dir.h"
#include "utils.h"
Path readlink(const Path &link)
{
- (void)link;
- throw unsupported("readlink");
+ DWORD attrs = GetFileAttributes(link.str().c_str());
+ if(!(attrs&FILE_ATTRIBUTE_REPARSE_POINT))
+ throw system_error("readlink", ERROR_NOT_A_REPARSE_POINT);
+
+ HANDLE handle = CreateFile(link.str().c_str(), READ_CONTROL, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
+ if(handle==INVALID_HANDLE_VALUE)
+ throw system_error("CreateFile");
+
+ char data[1024];
+ DWORD length;
+ bool success = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, data, sizeof(data), &length, nullptr);
+ CloseHandle(handle);
+ if(!success)
+ throw system_error("DeviceIoControl(FSCTL_GET_REPARSE_POINT)");
+
+ DWORD reparse_tag = *reinterpret_cast<DWORD *>(data);
+ if(reparse_tag!=IO_REPARSE_TAG_SYMLINK)
+ throw runtime_error("reparse point is not a symlink");
+
+ // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913
+ WORD start = 20+*reinterpret_cast<WORD *>(data+8);
+ WORD end = start+*reinterpret_cast<WORD *>(data+10);
+ return StringCodec::transcode<StringCodec::Utf16Le, StringCodec::Utf8>(string(data+start, data+end));
}
Path realpath(const Path &path)