/** * Given a directory and a version, find if the package relative * dir under the given directory contains hostpolicy.dll */ bool to_hostpolicy_package_dir(const pal::string_t& dir, const pal::string_t& version, pal::string_t* candidate) { assert(!version.empty()); candidate->clear(); // Ensure the relative dir contains platform directory separators. pal::string_t rel_dir = _STRINGIFY(HOST_POLICY_PKG_REL_DIR); if (DIR_SEPARATOR != '/') { replace_char(&rel_dir, '/', DIR_SEPARATOR); } // Construct the path to directory containing hostpolicy. pal::string_t path = dir; append_path(&path, _STRINGIFY(HOST_POLICY_PKG_NAME)); // package name append_path(&path, version.c_str()); // package version append_path(&path, rel_dir.c_str()); // relative dir containing hostpolicy library // Check if "path" contains the required library. if (!library_exists_in_dir(path, LIBHOSTPOLICY_NAME, nullptr)) { trace::verbose(_X("Did not find %s in directory %s"), LIBHOSTPOLICY_NAME, path.c_str()); return false; } // "path" contains the directory containing hostpolicy library. *candidate = path; trace::verbose(_X("Found %s in directory %s"), LIBHOSTPOLICY_NAME, path.c_str()); return true; }
/** * When the framework is not found, display detailed error message * about available frameworks and installation of new framework. */ void handle_missing_framework_error(const pal::string_t& fx_name, const pal::string_t& fx_version, const pal::string_t& fx_dir) { pal::string_t fx_ver_dirs = get_directory(fx_dir); // Display the error message about missing FX. trace::error(_X("The specified framework '%s', version '%s' was not found."), fx_name.c_str(), fx_version.c_str()); trace::error(_X(" - Check application dependencies and target a framework version installed at:")); trace::error(_X(" %s"), fx_ver_dirs.c_str()); // Gather the list of versions installed at the shared FX location. bool is_print_header = true; std::vector<pal::string_t> versions; pal::readdir(fx_ver_dirs, &versions); for (const auto& ver : versions) { // Make sure we filter out any non-version folders at shared FX location. fx_ver_t parsed(-1, -1, -1); if (fx_ver_t::parse(ver, &parsed, false)) { // Print banner only once before printing the versions if (is_print_header) { trace::error(_X(" - The following versions are installed:")); is_print_header = false; } trace::error(_X(" %s"), ver.c_str()); } } trace::error(_X(" - Alternatively, install the framework version '%s'."), fx_version.c_str()); }
void tpafile::write_tpa_list(pal::string_t& output) { std::set<pal::string_t> items; for (auto entry : m_entries) { if (pal::strcmp(entry.asset_type.c_str(), _X("runtime")) == 0 && items.find(entry.asset_name) == items.end()) { // Resolve the full path for (auto search_path : m_package_search_paths) { pal::string_t candidate; candidate.reserve(search_path.length() + entry.library_name.length() + entry.library_version.length() + entry.relative_path.length() + 3); candidate.append(search_path); append_path(candidate, entry.library_name.c_str()); append_path(candidate, entry.library_version.c_str()); append_path(candidate, entry.relative_path.c_str()); if (pal::file_exists(candidate)) { trace::verbose(_X("adding tpa entry: %s"), candidate.c_str()); output.append(candidate); output.push_back(PATH_SEPARATOR); items.insert(entry.asset_name); break; } } } } }
bool pal::file_exists(const pal::string_t& path) { if (path.empty()) { return false; } struct stat buffer; return (::stat(path.c_str(), &buffer) == 0); }
pal::string_t get_directory(const pal::string_t& path) { // Find the last dir separator auto path_sep = path.find_last_of(DIR_SEPARATOR); if (path_sep == pal::string_t::npos) { return pal::string_t(path); } return path.substr(0, path_sep); }
bool pal::touch_file(const pal::string_t& path) { int fd = open(path.c_str(), (O_CREAT | O_EXCL), (S_IRUSR | S_IRGRP | S_IROTH)); if (fd == -1) { trace::warning(_X("open(%s) failed in %s"), path.c_str(), _STRINGIFY(__FUNCTION__)); return false; } (void) close(fd); return true; }
bool try_stou(const pal::string_t& str, unsigned* num) { if (str.empty()) { return false; } if (str.find_first_not_of(_X("0123456789")) != pal::string_t::npos) { return false; } *num = (unsigned) std::stoul(str); return true; }
pal::string_t get_filename_without_ext(const pal::string_t& path) { if (path.empty()) { return path; } size_t name_pos = path.find_last_of(_X("/\\")); size_t dot_pos = path.rfind(_X('.')); size_t start_pos = (name_pos == pal::string_t::npos) ? 0 : (name_pos + 1); size_t count = (dot_pos == pal::string_t::npos || dot_pos < start_pos) ? pal::string_t::npos : (dot_pos - start_pos); return path.substr(start_pos, count); }
bool pal::pal_utf8string(const pal::string_t& str, std::vector<char>* out) { out->clear(); // Pass -1 as we want explicit null termination in the char buffer. size_t size = ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, nullptr, nullptr); if (size == 0) { return false; } out->resize(size, '\0'); return ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, out->data(), out->size(), nullptr, nullptr) != 0; }
/** * Resolve the hostpolicy version from deps. * - Scan the deps file's libraries section and find the hostpolicy version in the file. */ pal::string_t resolve_hostpolicy_version_from_deps(const pal::string_t& deps_json) { trace::verbose(_X("--- Resolving %s version from deps json [%s]"), LIBHOSTPOLICY_NAME, deps_json.c_str()); pal::string_t retval; if (!pal::file_exists(deps_json)) { trace::verbose(_X("Dependency manifest [%s] does not exist"), deps_json.c_str()); return retval; } pal::ifstream_t file(deps_json); if (!file.good()) { trace::verbose(_X("Dependency manifest [%s] could not be opened"), deps_json.c_str()); return retval; } if (skip_utf8_bom(&file)) { trace::verbose(_X("UTF-8 BOM skipped while reading [%s]"), deps_json.c_str()); } try { const auto root = json_value::parse(file); const auto& json = root.as_object(); const auto& libraries = json.at(_X("libraries")).as_object(); // Look up the root package instead of the "runtime" package because we can't do a full rid resolution. // i.e., look for "Microsoft.NETCore.DotNetHostPolicy/" followed by version. pal::string_t prefix = _X("Microsoft.NETCore.DotNetHostPolicy/"); for (const auto& library : libraries) { if (starts_with(library.first, prefix, false)) { // Extract the version information that occurs after '/' retval = library.first.substr(prefix.size()); break; } } } catch (const std::exception& je) { pal::string_t jes; (void)pal::utf8_palstring(je.what(), &jes); trace::error(_X("A JSON parsing exception occurred in [%s]: %s"), deps_json.c_str(), jes.c_str()); } trace::verbose(_X("Resolved version %s from dependency manifest file [%s]"), retval.c_str(), deps_json.c_str()); return retval; }
pal::string_t strip_file_ext(const pal::string_t& path) { if (path.empty()) { return path; } size_t sep_pos = path.rfind(_X("/\\")); size_t dot_pos = path.rfind(_X('.')); if (sep_pos != pal::string_t::npos && sep_pos > dot_pos) { return path; } return path.substr(0, dot_pos); }
/** * When the framework is not found, display detailed error message * about available frameworks and installation of new framework. */ void fx_resolver_t::display_missing_framework_error( const pal::string_t& fx_name, const pal::string_t& fx_version, const pal::string_t& fx_dir, const pal::string_t& dotnet_root) { std::vector<framework_info> framework_infos; pal::string_t fx_ver_dirs; if (fx_dir.length()) { fx_ver_dirs = fx_dir; framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, &framework_infos); } else { fx_ver_dirs = dotnet_root; } framework_info::get_all_framework_infos(dotnet_root, fx_name, &framework_infos); // Display the error message about missing FX. if (fx_version.length()) { trace::error(_X("The specified framework '%s', version '%s' was not found."), fx_name.c_str(), fx_version.c_str()); } else { trace::error(_X("The specified framework '%s' was not found."), fx_name.c_str()); } if (framework_infos.size()) { trace::error(_X(" - The following frameworks were found:")); for (const framework_info& info : framework_infos) { trace::error(_X(" %s at [%s]"), info.version.as_str().c_str(), info.path.c_str()); } } else { trace::error(_X(" - No frameworks were found.")); } trace::error(_X("")); trace::error(_X("You can resolve the problem by installing the specified framework and/or SDK.")); trace::error(_X("")); trace::error(_X("The .NET Core frameworks can be found at:")); trace::error(_X(" - %s"), DOTNET_CORE_DOWNLOAD_URL); }
/** * Given path to app binary, say app.dll or app.exe, retrieve the app.deps.json. */ pal::string_t get_deps_from_app_binary(const pal::string_t& app_base, const pal::string_t& app) { pal::string_t deps_file; auto app_name = get_filename(app); deps_file.reserve(app_base.length() + 1 + app_name.length() + 5); deps_file.append(app_base); if (!app_base.empty() && app_base.back() != DIR_SEPARATOR) { deps_file.push_back(DIR_SEPARATOR); } deps_file.append(app_name, 0, app_name.find_last_of(_X("."))); deps_file.append(_X(".deps.json")); return deps_file; }
pal::string_t trim_quotes(pal::string_t stringToCleanup) { pal::char_t quote_array[2] = {'\"', '\''}; for(int index = 0; index < sizeof(quote_array)/sizeof(quote_array[0]); index++) { size_t pos = stringToCleanup.find(quote_array[index]); while(pos != std::string::npos) { stringToCleanup = stringToCleanup.erase(pos, 1); pos = stringToCleanup.find(quote_array[index]); } } return stringToCleanup; }
void append_path(pal::string_t& path1, const pal::char_t* path2) { if (pal::is_path_rooted(path2)) { path1.assign(path2); } else { if (path1.back() != DIR_SEPARATOR) { path1.push_back(DIR_SEPARATOR); } path1.append(path2); } }
pal::string_t get_filename(const pal::string_t& path) { if (path.empty()) { return path; } auto name_pos = path.find_last_of(DIR_SEPARATOR); if (name_pos == pal::string_t::npos) { return path; } return path.substr(name_pos + 1); }
bool read_field(pal::string_t line, int& offset, pal::string_t& value_recv) { // The first character should be a '"' if (line[offset] != '"') { trace::error(_X("error reading TPA file")); return false; } offset++; // Set up destination buffer (it can't be bigger than the original line) pal::char_t buf[PATH_MAX]; auto buf_offset = 0; // Iterate through characters in the string for (; offset < line.length(); offset++) { // Is this a '\'? if (line[offset] == '\\') { // Skip this character and read the next character into the buffer offset++; buf[buf_offset] = line[offset]; } // Is this a '"'? else if (line[offset] == '\"') { // Done! Advance to the pointer after the input offset++; break; } else { // Take the character buf[buf_offset] = line[offset]; } buf_offset++; } buf[buf_offset] = '\0'; value_recv.assign(buf); // Consume the ',' if we have one if (line[offset] == ',') { offset++; } return true; }
bool LongFile::IsPathNotFullyQualified(const pal::string_t& path) { if (path.length() < 2) { return true; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less). } if (IsDirectorySeparator(path[0])) { return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes } return !((path.length() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- "i.e. C:\" && (path[1] == VolumeSeparatorChar) && IsDirectorySeparator(path[2])); }
int execute_app( const pal::string_t& impl_dll_dir, const corehost_init_t* init, const int argc, const pal::char_t* argv[]) { pal::dll_t corehost; corehost_main_fn host_main = nullptr; corehost_load_fn host_load = nullptr; corehost_unload_fn host_unload = nullptr; int code = load_host_library(impl_dll_dir, &corehost, &host_load, &host_main, &host_unload); if (code != StatusCode::Success) { trace::error(_X("Could not load host policy library [%s]"), impl_dll_dir.c_str()); return code; } if ((code = host_load(init)) == 0) { code = host_main(argc, argv); (void)host_unload(); } pal::unload_library(corehost); return code; }
// For some distros, we don't want to use the full version from VERSION_ID. One example is // Red Hat Enterprise Linux, which includes a minor version in their VERSION_ID but minor // versions are backwards compatable. // // In this case, we'll normalized RIDs like 'rhel.7.2' and 'rhel.7.3' to a generic // 'rhel.7'. This brings RHEL in line with other distros like CentOS or Debian which // don't put minor version numbers in their VERSION_ID fields because all minor versions // are backwards compatible. static pal::string_t normalize_linux_rid(pal::string_t rid) { pal::string_t rhelPrefix(_X("rhel.")); if (rid.compare(0, rhelPrefix.length(), rhelPrefix) == 0) { size_t minorVersionSeparatorIndex = rid.find(_X("."), rhelPrefix.length()); if (minorVersionSeparatorIndex != std::string::npos) { rid.erase(minorVersionSeparatorIndex, rid.length() - minorVersionSeparatorIndex); } } return rid; }
/** * Given a version and probing paths, find if package layout * directory containing hostpolicy exists. */ bool resolve_hostpolicy_dir_from_probe_paths(const pal::string_t& version, const std::vector<pal::string_t>& probe_realpaths, pal::string_t* candidate) { if (probe_realpaths.empty() || version.empty()) { return false; } // Check if the package relative directory containing hostpolicy exists. for (const auto& probe_path : probe_realpaths) { trace::verbose(_X("Considering %s to probe for %s"), probe_path.c_str(), LIBHOSTPOLICY_NAME); if (to_hostpolicy_package_dir(probe_path, version, candidate)) { return true; } } // Print detailed message about the file not found in the probe paths. trace::error(_X("Could not find required library %s in %d probing paths:"), LIBHOSTPOLICY_NAME, probe_realpaths.size()); for (const auto& path : probe_realpaths) { trace::error(_X(" %s"), path.c_str()); } return false; }
int load_host_library_common( const pal::string_t& lib_dir, pal::string_t& host_path, pal::dll_t* h_host, corehost_load_fn* load_fn, corehost_unload_fn* unload_fn) { if (!library_exists_in_dir(lib_dir, LIBHOSTPOLICY_NAME, &host_path)) { return StatusCode::CoreHostLibMissingFailure; } // Load library if (!pal::load_library(&host_path, h_host)) { trace::info(_X("Load library of %s failed"), host_path.c_str()); return StatusCode::CoreHostLibLoadFailure; } // Obtain entrypoint symbols *load_fn = (corehost_load_fn)pal::get_symbol(*h_host, "corehost_load"); *unload_fn = (corehost_unload_fn)pal::get_symbol(*h_host, "corehost_unload"); return (*load_fn != nullptr) && (*unload_fn != nullptr) ? StatusCode::Success : StatusCode::CoreHostEntryPointFailure; }
void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vector<pal::string_t>* locations) { bool multilevel_lookup = multilevel_lookup_enabled(); // Multi-level lookup will look for the most appropriate version in several locations // by following the priority rank below: // .exe directory // Global .NET directories // If it is not activated, then only .exe directory will be considered pal::string_t dotnet_dir_temp; if (!dotnet_dir.empty()) { // own_dir contains DIR_SEPARATOR appended that we need to remove. dotnet_dir_temp = dotnet_dir; remove_trailing_dir_seperator(&dotnet_dir_temp); locations->push_back(dotnet_dir_temp); } std::vector<pal::string_t> global_dirs; if (multilevel_lookup && pal::get_global_dotnet_dirs(&global_dirs)) { for (pal::string_t dir : global_dirs) { // avoid duplicate paths if (!pal::are_paths_equal_with_normalized_casing(dir, dotnet_dir_temp)) { locations->push_back(dir); } } } }
// ----------------------------------------------------------------------------- // Load local assemblies by priority order of their file extensions and // unique-fied by their simple name. // void deps_resolver_t::get_dir_assemblies( const pal::string_t& dir, const pal::string_t& dir_name, dir_assemblies_t* dir_assemblies) { trace::verbose(_X("Adding files from %s dir %s"), dir_name.c_str(), dir.c_str()); // Managed extensions in priority order, pick DLL over EXE and NI over IL. const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") }; // List of files in the dir std::vector<pal::string_t> files; pal::readdir(dir, &files); for (const auto& ext : managed_ext) { for (const auto& file : files) { // Nothing to do if file length is smaller than expected ext. if (file.length() <= ext.length()) { continue; } auto file_name = file.substr(0, file.length() - ext.length()); auto file_ext = file.substr(file_name.length()); // Ext did not match expected ext, skip this file. if (pal::strcasecmp(file_ext.c_str(), ext.c_str())) { continue; } // Already added entry for this asset, by priority order skip this ext if (dir_assemblies->count(file_name)) { trace::verbose(_X("Skipping %s because the %s already exists in %s assemblies"), file.c_str(), dir_assemblies->find(file_name)->second.c_str(), dir_name.c_str()); continue; } // Add entry for this asset pal::string_t file_path = dir + DIR_SEPARATOR + file; trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str()); dir_assemblies->emplace(file_name, file_path); } } }
runtime_config_t::runtime_config_t(const pal::string_t& path) : m_fx_roll_fwd(true) , m_path(path) , m_portable(false) { m_valid = ensure_parsed(); trace::verbose(_X("Runtime config [%s] is valid=[%d]"), path.c_str(), m_valid); }
// ----------------------------------------------------------------------------- // Load the deps file and parse its "entry" lines which contain the "fields" of // the entry. Populate an array of these entries. // bool deps_json_t::load(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph) { m_file_exists = pal::file_exists(deps_path); // If file doesn't exist, then assume parsed. if (!m_file_exists) { trace::verbose(_X("Could not locate the dependencies manifest file [%s]. Some libraries may fail to resolve."), deps_path.c_str()); return true; } // Somehow the file stream could not be opened. This is an error. pal::ifstream_t file(deps_path); if (!file.good()) { trace::error(_X("Could not open dependencies manifest file [%s]"), deps_path.c_str()); return false; } if (skip_utf8_bom(&file)) { trace::verbose(_X("UTF-8 BOM skipped while reading [%s]"), deps_path.c_str()); } try { const auto json = json_value::parse(file); const auto& runtime_target = json.at(_X("runtimeTarget")); const pal::string_t& name = runtime_target.is_string()? runtime_target.as_string(): runtime_target.at(_X("name")).as_string(); trace::verbose(_X("Loading deps file... %s as portable=[%d]"), deps_path.c_str(), portable); return (portable) ? load_portable(json, name, rid_fallback_graph) : load_standalone(json, name); } catch (const std::exception& je) { pal::string_t jes; (void) pal::utf8_palstring(je.what(), &jes); trace::error(_X("A JSON parsing exception occurred in [%s]: %s"), deps_path.c_str(), jes.c_str()); return false; } }
bool get_global_shared_store_dirs(std::vector<pal::string_t>* dirs, const pal::string_t& arch, const pal::string_t& tfm) { std::vector<pal::string_t> global_dirs; if (!pal::get_global_dotnet_dirs(&global_dirs)) { return false; } for (pal::string_t dir : global_dirs) { append_path(&dir, RUNTIME_STORE_DIRECTORY_NAME); append_path(&dir, arch.c_str()); append_path(&dir, tfm.c_str()); dirs->push_back(dir); } return true; }
void tpafile::write_native_paths(pal::string_t& output) { std::set<pal::string_t> items; for (auto search_path : m_native_search_paths) { if (items.find(search_path) == items.end()) { trace::verbose(_X("adding native search path: %s"), search_path.c_str()); output.append(search_path); output.push_back(PATH_SEPARATOR); items.insert(search_path); } } for (auto entry : m_entries) { auto dir = entry.relative_path.substr(0, entry.relative_path.find_last_of(DIR_SEPARATOR)); if (pal::strcmp(entry.asset_type.c_str(), _X("native")) == 0 && items.find(dir) == items.end()) { // Resolve the full path for (auto search_path : m_package_search_paths) { pal::string_t candidate; candidate.reserve(search_path.length() + entry.library_name.length() + entry.library_version.length() + dir.length() + 3); candidate.append(search_path); append_path(candidate, entry.library_name.c_str()); append_path(candidate, entry.library_version.c_str()); append_path(candidate, get_directory(entry.relative_path).c_str()); if (pal::file_exists(candidate)) { trace::verbose(_X("adding native search path: %s"), candidate.c_str()); output.append(candidate); output.push_back(PATH_SEPARATOR); items.insert(dir); break; } } } } }
pal::string_t resolve_sdk_version(pal::string_t sdk_path) { trace::verbose(_X("--- Resolving SDK version from SDK dir [%s]"), sdk_path.c_str()); pal::string_t retval; std::vector<pal::string_t> versions; pal::readdir(sdk_path, &versions); fx_ver_t max_ver(-1, -1, -1); fx_ver_t max_pre(-1, -1, -1); for (const auto& version : versions) { trace::verbose(_X("Considering version... [%s]"), version.c_str()); fx_ver_t ver(-1, -1, -1); if (fx_ver_t::parse(version, &ver, true)) { max_ver = std::max(ver, max_ver); } if (fx_ver_t::parse(version, &ver, false)) { max_pre = std::max(ver, max_pre); } } // No production, use the max pre-release. if (max_ver == fx_ver_t(-1, -1, -1)) { trace::verbose(_X("No production version found, so using latest prerelease")); max_ver = max_pre; } pal::string_t max_ver_str = max_ver.as_str(); append_path(&sdk_path, max_ver_str.c_str()); trace::verbose(_X("Checking if resolved SDK dir [%s] exists"), sdk_path.c_str()); if (pal::directory_exists(sdk_path)) { retval = sdk_path; } trace::verbose(_X("Resolved SDK dir is [%s]"), retval.c_str()); return retval; }
void tpafile::add_from_local_dir(const pal::string_t& dir) { trace::verbose(_X("adding files from %s to TPA"), dir.c_str()); const pal::char_t * const tpa_extensions[] = { _X(".ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir _X(".dll"), _X(".ni.exe"), _X(".exe"), }; std::set<pal::string_t> added_assemblies; // Get directory entries auto files = pal::readdir(dir); for (auto ext : tpa_extensions) { auto len = pal::strlen(ext); for (auto file : files) { // Can't be a match if it's the same length as the extension :) if (file.length() > len) { // Extract the same amount of text from the end of file name auto file_ext = file.substr(file.length() - len, len); // Check if this file name matches if (pal::strcasecmp(ext, file_ext.c_str()) == 0) { // Get the assembly name by stripping the extension // and add it to the set so we can de-dupe auto asm_name = file.substr(0, file.length() - len); // TODO(anurse): Also check if already in TPA file if (added_assemblies.find(asm_name) == added_assemblies.end()) { added_assemblies.insert(asm_name); tpaentry_t entry; entry.asset_type = pal::string_t(_X("runtime")); entry.library_name = pal::string_t(asm_name); entry.library_version = pal::string_t(_X("")); pal::string_t relpath(dir); relpath.push_back(DIR_SEPARATOR); relpath.append(file); entry.relative_path = relpath; entry.asset_name = asm_name; trace::verbose(_X("adding %s to TPA list from %s"), asm_name.c_str(), relpath.c_str()); m_entries.push_back(entry); } } } } } }