Index: include/lldb/Host/FileSystem.h =================================================================== --- include/lldb/Host/FileSystem.h +++ include/lldb/Host/FileSystem.h @@ -32,6 +32,7 @@ static lldb::user_id_t GetFileSize(const FileSpec &file_spec); static bool GetFileExists(const FileSpec &file_spec); + static bool CanCreateSymlinks(); static Error Symlink(const char *src, const char *dst); static Error Readlink(const char *path, char *buf, size_t buf_len); static Error Unlink(const char *path); Index: source/Host/posix/FileSystem.cpp =================================================================== --- source/Host/posix/FileSystem.cpp +++ source/Host/posix/FileSystem.cpp @@ -148,6 +148,12 @@ return file_spec.Exists(); } +bool +FileSystem::CanCreateSymlinks() +{ + return true; +} + Error FileSystem::Symlink(const char *src, const char *dst) { Index: source/Host/windows/FileSystem.cpp =================================================================== --- source/Host/windows/FileSystem.cpp +++ source/Host/windows/FileSystem.cpp @@ -11,11 +11,73 @@ #include +#include +#include + +#include "lldb/Core/Log.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Host/windows/AutoHandle.h" #include "llvm/Support/FileSystem.h" using namespace lldb_private; +namespace +{ + +Error EnableCreateSymLinkPrivilege() +{ + HANDLE hProcessToken; + + // Get a token for this process. + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + return Error(::GetLastError(), lldb::eErrorTypeWin32); + AutoHandle process_token(hProcessToken); + + // Get the LUID for the SE_CREATE_SYMBOLIC_LINK privilege. + LUID luid = {0}; + if (!::LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) + return Error(::GetLastError(), lldb::eErrorTypeWin32); + + DWORD buffer_len; + if (!::GetTokenInformation(hProcessToken, TokenPrivileges, NULL, 0, &buffer_len)) + { + const auto err = ::GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) + return Error(err, lldb::eErrorTypeWin32); + } + + std::vector buffer(buffer_len); + if (!::GetTokenInformation(hProcessToken, TokenPrivileges, &buffer[0], buffer_len, &buffer_len)) + return Error(::GetLastError(), lldb::eErrorTypeWin32); + + // Iterate through all the privileges. + bool found = false; + auto privs_ptr = reinterpret_cast(&buffer[0]); + for (DWORD i = 0; i < privs_ptr->PrivilegeCount; i++) + { + if (!memcmp(&privs_ptr->Privileges[i].Luid, &luid, sizeof(LUID))) + { + found = true; + break; + } + } + if (!found) + return Error("Process token doesn't have SE_CREATE_SYMBOLIC_LINK privilege"); + + TOKEN_PRIVILEGES tkp = {0}; + tkp.PrivilegeCount = 1; // one privilege to set + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tkp.Privileges[0].Luid = luid; + + // Enable privilege for this process. + if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, reinterpret_cast(NULL), 0)) + return Error(::GetLastError(), lldb::eErrorTypeWin32); + + return Error(); +} + +} // namespace + FileSpec::PathSyntax FileSystem::GetNativePathSyntax() { @@ -95,9 +157,29 @@ return file_spec.Exists(); } +bool +FileSystem::CanCreateSymlinks() +{ + static std::once_flag g_cancreatesymlinks_init_flag; + static bool g_cancreatesymlinks = false; + + std::call_once(g_cancreatesymlinks_init_flag, []() { + const auto error = EnableCreateSymLinkPrivilege(); + g_cancreatesymlinks = error.Success(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST)); + if (log) + log->Printf("Cannot create sym links: %s", error.AsCString()); + }); + return g_cancreatesymlinks; +} + Error FileSystem::Symlink(const char *linkname, const char *target) { + if (!CanCreateSymlinks()) + return Error("Process isn't allowed to create symlinks."); + Error error; DWORD attrib = ::GetFileAttributes(target); if (attrib == INVALID_FILE_ATTRIBUTES) Index: source/Utility/ModuleCache.cpp =================================================================== --- source/Utility/ModuleCache.cpp +++ source/Utility/ModuleCache.cpp @@ -70,6 +70,9 @@ return Error ("Failed to rename file %s to %s: %s", tmp_file_path.c_str (), module_file_path.GetPath ().c_str (), err_code.message ().c_str ()); + if (!FileSystem::CanCreateSymlinks()) + return Error(); + // Create sysroot link to a module. const auto sysroot_module_path_spec = GetHostSysRootModulePath (root_dir_spec, hostname, module_spec.GetFileSpec ()); return CreateHostSysRootModuleSymLink (sysroot_module_path_spec, module_file_path); @@ -99,10 +102,13 @@ if (module_file_path.GetByteSize () != module_spec.GetObjectSize ()) return Error ("Module %s has invalid file size", module_file_path.GetPath ().c_str ()); - // We may have already cached module but downloaded from an another host - in this case let's create a symlink to it. - const auto sysroot_module_path_spec = GetHostSysRootModulePath (root_dir_spec, hostname, module_spec.GetFileSpec ()); - if (!sysroot_module_path_spec.Exists ()) - CreateHostSysRootModuleSymLink (sysroot_module_path_spec, module_spec.GetFileSpec ()); + if (FileSystem::CanCreateSymlinks()) + { + // We may have already cached module but downloaded from an another host - in this case let's create a symlink to it. + const auto sysroot_module_path_spec = GetHostSysRootModulePath(root_dir_spec, hostname, module_spec.GetFileSpec()); + if (!sysroot_module_path_spec.Exists()) + CreateHostSysRootModuleSymLink(sysroot_module_path_spec, module_spec.GetFileSpec()); + } auto cached_module_spec (module_spec); cached_module_spec.GetUUID ().Clear (); // Clear UUID since it may contain md5 content hash instead of real UUID.