Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeandro A. F. Pereira <leandro.pereira@microsoft.com>2020-02-08 03:29:12 +0300
committerGitHub <noreply@github.com>2020-02-08 03:29:12 +0300
commit9013928b6dabbac9469b8d069e89ffb58c363941 (patch)
treea0a8f466159947d1052222194f2f96b05be3e219 /src/installer/corehost/cli/hostmisc/pal.unix.cpp
parentd9d4401499b5997bbf3e73d27f142e49b0e14184 (diff)
corehost: Build intermediate library to avoid building twice (#2123)
Some files are built twice in the hosting layer, so create two separate static libraries that are then linked with all targets that need them. Fixes [core-setup/8276](https://github.com/dotnet/core-setup/issues/8276).
Diffstat (limited to 'src/installer/corehost/cli/hostmisc/pal.unix.cpp')
-rw-r--r--src/installer/corehost/cli/hostmisc/pal.unix.cpp863
1 files changed, 863 insertions, 0 deletions
diff --git a/src/installer/corehost/cli/hostmisc/pal.unix.cpp b/src/installer/corehost/cli/hostmisc/pal.unix.cpp
new file mode 100644
index 00000000000..0fc00d26893
--- /dev/null
+++ b/src/installer/corehost/cli/hostmisc/pal.unix.cpp
@@ -0,0 +1,863 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "pal.h"
+#include "utils.h"
+#include "trace.h"
+
+#include <cassert>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <ctime>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__LINUX__)
+#define symlinkEntrypointExecutable "/proc/self/exe"
+#elif !defined(__APPLE__)
+#define symlinkEntrypointExecutable "/proc/curproc/exe"
+#endif
+
+pal::string_t pal::to_string(int value) { return std::to_string(value); }
+
+pal::string_t pal::to_lower(const pal::string_t& in)
+{
+ pal::string_t ret = in;
+ std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+ return ret;
+}
+
+pal::string_t pal::get_timestamp()
+{
+ std::time_t t = std::time(nullptr);
+ const std::size_t elems = 100;
+ char_t buf[elems];
+ std::strftime(buf, elems, _X("%c %Z"), std::gmtime(&t));
+
+ return pal::string_t(buf);
+}
+
+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;
+}
+
+void* pal::map_file_readonly(const pal::string_t& path, size_t& length)
+{
+ int fd = open(path.c_str(), O_RDONLY, (S_IRUSR | S_IRGRP | S_IROTH));
+ if (fd == -1)
+ {
+ trace::warning(_X("Failed to map file. open(%s) failed with error %d"), path.c_str(), errno);
+ return nullptr;
+ }
+
+ struct stat buf;
+ if (fstat(fd, &buf) != 0)
+ {
+ trace::warning(_X("Failed to map file. fstat(%s) failed with error %d"), path.c_str(), errno);
+ close(fd);
+ return nullptr;
+ }
+
+ length = buf.st_size;
+ void* address = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, 0);
+
+ if(address == nullptr)
+ {
+ trace::warning(_X("Failed to map file. mmap(%s) failed with error %d"), path.c_str(), errno);
+ close(fd);
+ return nullptr;
+ }
+
+ close(fd);
+ return address;
+}
+
+bool pal::getcwd(pal::string_t* recv)
+{
+ recv->clear();
+ pal::char_t* buf = ::getcwd(nullptr, 0);
+ if (buf == nullptr)
+ {
+ if (errno == ENOENT)
+ {
+ return false;
+ }
+
+ trace::error(_X("getcwd() failed: %s"), strerror(errno));
+ return false;
+ }
+ recv->assign(buf);
+ ::free(buf);
+ return true;
+}
+
+namespace
+{
+ bool get_loaded_library_from_proc_maps(const pal::char_t *library_name, pal::dll_t *dll, pal::string_t *path)
+ {
+ char *line = nullptr;
+ size_t lineLen = 0;
+ ssize_t read;
+ FILE *file = pal::file_open(_X("/proc/self/maps"), _X("r"));
+ if (file == nullptr)
+ return false;
+
+ // Read maps file line by line to check fo the library
+ bool found = false;
+ pal::string_t path_local;
+ while ((read = getline(&line, &lineLen, file)) != -1)
+ {
+ char buf[PATH_MAX];
+ if (sscanf(line, "%*p-%*p %*[-rwxsp] %*p %*[:0-9a-f] %*d %s\n", buf) == 1)
+ {
+ path_local = buf;
+ size_t pos = path_local.rfind(DIR_SEPARATOR);
+ if (pos == std::string::npos)
+ continue;
+
+ pos = path_local.find(library_name, pos);
+ if (pos != std::string::npos)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ fclose(file);
+ if (!found)
+ return false;
+
+ pal::dll_t dll_maybe = dlopen(path_local.c_str(), RTLD_LAZY | RTLD_NOLOAD);
+ if (dll_maybe == nullptr)
+ return false;
+
+ *dll = dll_maybe;
+ path->assign(path_local);
+ return true;
+ }
+}
+
+bool pal::get_loaded_library(
+ const char_t *library_name,
+ const char *symbol_name,
+ /*out*/ dll_t *dll,
+ /*out*/ pal::string_t *path)
+{
+ pal::string_t library_name_local;
+#if defined(__APPLE__)
+ if (!pal::is_path_rooted(library_name))
+ library_name_local.append("@rpath/");
+#endif
+ library_name_local.append(library_name);
+
+ dll_t dll_maybe = dlopen(library_name_local.c_str(), RTLD_LAZY | RTLD_NOLOAD);
+ if (dll_maybe == nullptr)
+ {
+ if (pal::is_path_rooted(library_name))
+ return false;
+
+ // dlopen on some systems only finds loaded libraries when given the full path
+ // Check proc maps as a fallback
+ return get_loaded_library_from_proc_maps(library_name, dll, path);
+ }
+
+ // Not all systems support getting the path from just the handle (e.g. dlinfo),
+ // so we rely on the caller passing in a symbol name so that we get (any) address
+ // in the library
+ assert(symbol_name != nullptr);
+ pal::proc_t proc = pal::get_symbol(dll_maybe, symbol_name);
+ Dl_info info;
+ if (dladdr(proc, &info) == 0)
+ return false;
+
+ *dll = dll_maybe;
+ path->assign(info.dli_fname);
+ return true;
+}
+
+bool pal::load_library(const string_t* path, dll_t* dll)
+{
+ *dll = dlopen(path->c_str(), RTLD_LAZY);
+ if (*dll == nullptr)
+ {
+ trace::error(_X("Failed to load %s, error: %s"), path, dlerror());
+ return false;
+ }
+ return true;
+}
+
+pal::proc_t pal::get_symbol(dll_t library, const char* name)
+{
+ auto result = dlsym(library, name);
+ if (result == nullptr)
+ {
+ trace::info(_X("Probed for and did not find library symbol %s, error: %s"), name, dlerror());
+ }
+
+ return result;
+}
+
+void pal::unload_library(dll_t library)
+{
+ if (dlclose(library) != 0)
+ {
+ trace::warning(_X("Failed to unload library, error: %s"), dlerror());
+ }
+}
+
+int pal::xtoi(const char_t* input)
+{
+ return atoi(input);
+}
+
+bool pal::is_path_rooted(const pal::string_t& path)
+{
+ return path.front() == '/';
+}
+
+bool pal::get_default_breadcrumb_store(string_t* recv)
+{
+ recv->clear();
+ pal::string_t ext;
+ if (pal::getenv(_X("CORE_BREADCRUMBS"), &ext) && pal::realpath(&ext))
+ {
+ // We should have the path in ext.
+ trace::info(_X("Realpath CORE_BREADCRUMBS [%s]"), ext.c_str());
+ }
+
+ if (!pal::directory_exists(ext))
+ {
+ trace::info(_X("Directory core breadcrumbs [%s] was not specified or found"), ext.c_str());
+ ext.clear();
+ append_path(&ext, _X("opt"));
+ append_path(&ext, _X("corebreadcrumbs"));
+ if (!pal::directory_exists(ext))
+ {
+ trace::info(_X("Fallback directory core breadcrumbs at [%s] was not found"), ext.c_str());
+ return false;
+ }
+ }
+
+ if (access(ext.c_str(), (R_OK | W_OK)) != 0)
+ {
+ trace::info(_X("Breadcrumb store [%s] is not ACL-ed with rw-"), ext.c_str());
+ }
+
+ recv->assign(ext);
+ return true;
+}
+
+bool pal::get_default_servicing_directory(string_t* recv)
+{
+ recv->clear();
+ pal::string_t ext;
+ if (pal::getenv(_X("CORE_SERVICING"), &ext) && pal::realpath(&ext))
+ {
+ // We should have the path in ext.
+ trace::info(_X("Realpath CORE_SERVICING [%s]"), ext.c_str());
+ }
+
+ if (!pal::directory_exists(ext))
+ {
+ trace::info(_X("Directory core servicing at [%s] was not specified or found"), ext.c_str());
+ ext.clear();
+ append_path(&ext, _X("opt"));
+ append_path(&ext, _X("coreservicing"));
+ if (!pal::directory_exists(ext))
+ {
+ trace::info(_X("Fallback directory core servicing at [%s] was not found"), ext.c_str());
+ return false;
+ }
+ }
+
+ if (access(ext.c_str(), R_OK) != 0)
+ {
+ trace::info(_X("Directory core servicing at [%s] was not ACL-ed properly"), ext.c_str());
+ }
+
+ recv->assign(ext);
+ trace::info(_X("Using core servicing at [%s]"), ext.c_str());
+ return true;
+}
+
+bool is_read_write_able_directory(pal::string_t& dir)
+{
+ return pal::realpath(&dir) &&
+ (access(dir.c_str(), R_OK | W_OK | X_OK) == 0);
+}
+
+bool pal::get_temp_directory(pal::string_t& tmp_dir)
+{
+ // First, check for the POSIX standard environment variable
+ if (pal::getenv(_X("TMPDIR"), &tmp_dir))
+ {
+ return is_read_write_able_directory(tmp_dir);
+ }
+
+ // On non-compliant systems (ex: Ubuntu) try /var/tmp or /tmp directories.
+ // /var/tmp is prefered since its contents are expected to survive across
+ // machine reboot.
+ pal::string_t _var_tmp = _X("/var/tmp/");
+ if (is_read_write_able_directory(_var_tmp))
+ {
+ tmp_dir.assign(_var_tmp);
+ return true;
+ }
+
+ pal::string_t _tmp = _X("/tmp/");
+ if (is_read_write_able_directory(_tmp))
+ {
+ tmp_dir.assign(_tmp);
+ return true;
+ }
+
+ return false;
+}
+
+bool pal::get_global_dotnet_dirs(std::vector<pal::string_t>* recv)
+{
+ // No support for global directories in Unix.
+ return false;
+}
+
+bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv)
+{
+ *recv = _X("/etc/dotnet/install_location");
+
+ // ***Used only for testing***
+ pal::string_t environment_install_location_override;
+ if (test_only_getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override))
+ {
+ *recv = environment_install_location_override;
+ }
+
+ return true;
+}
+
+bool pal::get_dotnet_self_registered_dir(pal::string_t* recv)
+{
+ recv->clear();
+
+ // ***Used only for testing***
+ pal::string_t environment_override;
+ if (test_only_getenv(_X("_DOTNET_TEST_GLOBALLY_REGISTERED_PATH"), &environment_override))
+ {
+ recv->assign(environment_override);
+ return true;
+ }
+ // ***************************
+
+ pal::string_t install_location_file_path;
+ if (!get_dotnet_self_registered_config_location(&install_location_file_path))
+ {
+ return false;
+ }
+ // ***************************
+
+ trace::verbose(_X("Looking for install_location file in '%s'."), install_location_file_path.c_str());
+ FILE* install_location_file = pal::file_open(install_location_file_path, "r");
+ if (install_location_file == nullptr)
+ {
+ trace::verbose(_X("The install_location file failed to open."));
+ return false;
+ }
+
+ bool result = false;
+
+ char buf[PATH_MAX];
+ char* install_location = fgets(buf, sizeof(buf), install_location_file);
+ if (install_location != nullptr)
+ {
+ size_t len = pal::strlen(install_location);
+
+ // fgets includes the newline character in the string - so remove it.
+ if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n')
+ {
+ install_location[len - 1] = '\0';
+ }
+
+ trace::verbose(_X("Using install location '%s'."), install_location);
+ *recv = install_location;
+ result = true;
+ }
+ else
+ {
+ trace::verbose(_X("The install_location file first line could not be read."));
+ }
+
+ fclose(install_location_file);
+ return result;
+}
+
+bool pal::get_default_installation_dir(pal::string_t* recv)
+{
+ // ***Used only for testing***
+ pal::string_t environmentOverride;
+ if (test_only_getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride))
+ {
+ recv->assign(environmentOverride);
+ return true;
+ }
+ // ***************************
+
+#if defined(__APPLE__)
+ recv->assign(_X("/usr/local/share/dotnet"));
+#else
+ recv->assign(_X("/usr/share/dotnet"));
+#endif
+ return true;
+}
+
+pal::string_t trim_quotes(pal::string_t stringToCleanup)
+{
+ pal::char_t quote_array[2] = {'\"', '\''};
+ for(size_t 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;
+}
+
+#if defined(__APPLE__)
+pal::string_t pal::get_current_os_rid_platform()
+{
+ pal::string_t ridOS;
+
+ char str[256];
+
+ // There is no good way to get the visible version of OSX (i.e. something like 10.x.y) as
+ // certain APIs work till 10.9 and have been deprecated and others require linking against
+ // UI frameworks to get the data.
+ //
+ // We will, instead, use kern.osrelease and use its major version number
+ // as a means to formulate the OSX 10.X RID.
+ //
+ // Needless to say, this will need to be updated if OSX RID were to become 11.* ever.
+ size_t size = sizeof(str);
+ int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0);
+ if (ret == 0)
+ {
+ std::string release(str, size);
+ size_t pos = release.find('.');
+ if (pos != std::string::npos)
+ {
+ // Extract the major version and subtract 4 from it
+ // to get the Minor version used in OSX versioning scheme.
+ // That is, given a version 10.X.Y, we will get X below.
+ int minorVersion = stoi(release.substr(0, pos)) - 4;
+ if (minorVersion < 10)
+ {
+ // On OSX, our minimum supported RID is 10.12.
+ minorVersion = 12;
+ }
+
+ ridOS.append(_X("osx.10."));
+ ridOS.append(pal::to_string(minorVersion));
+ }
+ }
+
+ return ridOS;
+}
+#elif defined(__FreeBSD__)
+// On FreeBSD get major verion. Minors should be compatible
+pal::string_t pal::get_current_os_rid_platform()
+{
+ pal::string_t ridOS;
+
+ char str[256];
+
+ size_t size = sizeof(str);
+ int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
+ if (ret == 0)
+ {
+ char *pos = strchr(str,'.');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ ridOS.append(_X("freebsd."));
+ ridOS.append(str);
+ }
+
+ return ridOS;
+}
+#else
+// 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."));
+ pal::string_t alpinePrefix(_X("alpine."));
+ size_t lastVersionSeparatorIndex = std::string::npos;
+
+ if (rid.compare(0, rhelPrefix.length(), rhelPrefix) == 0)
+ {
+ lastVersionSeparatorIndex = rid.find(_X("."), rhelPrefix.length());
+ }
+ else if (rid.compare(0, alpinePrefix.length(), alpinePrefix) == 0)
+ {
+ size_t secondVersionSeparatorIndex = rid.find(_X("."), alpinePrefix.length());
+ if (secondVersionSeparatorIndex != std::string::npos)
+ {
+ lastVersionSeparatorIndex = rid.find(_X("."), secondVersionSeparatorIndex + 1);
+ }
+ }
+
+ if (lastVersionSeparatorIndex != std::string::npos)
+ {
+ rid.erase(lastVersionSeparatorIndex, rid.length() - lastVersionSeparatorIndex);
+ }
+
+ return rid;
+}
+
+pal::string_t pal::get_current_os_rid_platform()
+{
+ pal::string_t ridOS;
+ pal::string_t versionFile(_X("/etc/os-release"));
+
+ if (pal::file_exists(versionFile))
+ {
+ // Read the file to get ID and VERSION_ID data that will be used
+ // to construct the RID.
+ std::fstream fsVersionFile;
+
+ fsVersionFile.open(versionFile, std::fstream::in);
+
+ // Proceed only if we were able to open the file
+ if (fsVersionFile.good())
+ {
+ pal::string_t line;
+ pal::string_t strID(_X("ID="));
+ pal::string_t valID;
+ pal::string_t strVersionID(_X("VERSION_ID="));
+ pal::string_t valVersionID;
+
+ bool fFoundID = false, fFoundVersion = false;
+
+ // Read the first line
+ std::getline(fsVersionFile, line);
+
+ // Loop until we are at the end of file
+ while (!fsVersionFile.eof())
+ {
+ // Look for ID if we have not found it already
+ if (!fFoundID)
+ {
+ size_t pos = line.find(strID);
+ if ((pos != std::string::npos) && (pos == 0))
+ {
+ valID.append(line.substr(3));
+ fFoundID = true;
+ }
+ }
+
+ // Look for VersionID if we have not found it already
+ if (!fFoundVersion)
+ {
+ size_t pos = line.find(strVersionID);
+ if ((pos != std::string::npos) && (pos == 0))
+ {
+ valVersionID.append(line.substr(11));
+ fFoundVersion = true;
+ }
+ }
+
+ if (fFoundID && fFoundVersion)
+ {
+ // We have everything we need to form the RID - break out of the loop.
+ break;
+ }
+
+ // Read the next line
+ std::getline(fsVersionFile, line);
+ }
+
+ // Close the file now that we are done with it.
+ fsVersionFile.close();
+
+ if (fFoundID)
+ {
+ ridOS.append(valID);
+ }
+
+ if (fFoundVersion)
+ {
+ ridOS.append(_X("."));
+ ridOS.append(valVersionID);
+ }
+
+ if (fFoundID || fFoundVersion)
+ {
+ // Remove any double-quotes
+ ridOS = trim_quotes(ridOS);
+ }
+ }
+ }
+
+ return normalize_linux_rid(ridOS);
+}
+#endif
+
+#if defined(__APPLE__)
+bool pal::get_own_executable_path(pal::string_t* recv)
+{
+ uint32_t path_length = 0;
+ if (_NSGetExecutablePath(nullptr, &path_length) == -1)
+ {
+ char path_buf[path_length];
+ if (_NSGetExecutablePath(path_buf, &path_length) == 0)
+ {
+ recv->assign(path_buf);
+ return true;
+ }
+ }
+ return false;
+}
+#elif defined(__FreeBSD__)
+bool pal::get_own_executable_path(pal::string_t* recv)
+{
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ char buf[PATH_MAX];
+ size_t cb = sizeof(buf);
+ int error_code = 0;
+ error_code = sysctl(mib, 4, buf, &cb, NULL, 0);
+ if (error_code == 0)
+ {
+ recv->assign(buf);
+ return true;
+ }
+
+ // ENOMEM
+ if (error_code == ENOMEM)
+ {
+ size_t len = sysctl(mib, 4, NULL, NULL, NULL, 0);
+ std::unique_ptr<char[]> buffer (new (std::nothrow) char[len]);
+
+ if (buffer == NULL)
+ {
+ return false;
+ }
+
+ error_code = sysctl(mib, 4, buffer.get(), &len, NULL, 0);
+ if (error_code == 0)
+ {
+ recv->assign(buffer.get());
+ return true;
+ }
+ }
+ return false;
+}
+#else
+bool pal::get_own_executable_path(pal::string_t* recv)
+{
+ // Just return the symlink to the exe from /proc
+ // We'll call realpath on it later
+ recv->assign(symlinkEntrypointExecutable);
+ return true;
+}
+#endif
+
+bool pal::get_own_module_path(string_t* recv)
+{
+ Dl_info info;
+ if (dladdr((void *)&pal::get_own_module_path, &info) == 0)
+ return false;
+
+ recv->assign(info.dli_fname);
+ return true;
+}
+
+bool pal::get_module_path(dll_t module, string_t* recv)
+{
+ return false;
+}
+
+bool pal::get_current_module(dll_t *mod)
+{
+ return false;
+}
+
+// Returns true only if an env variable can be read successfully to be non-empty.
+bool pal::getenv(const pal::char_t* name, pal::string_t* recv)
+{
+ recv->clear();
+
+ auto result = ::getenv(name);
+ if (result != nullptr)
+ {
+ recv->assign(result);
+ }
+
+ return (recv->length() > 0);
+}
+
+bool pal::realpath(pal::string_t* path, bool skip_error_logging)
+{
+ auto resolved = ::realpath(path->c_str(), nullptr);
+ if (resolved == nullptr)
+ {
+ if (errno == ENOENT)
+ {
+ return false;
+ }
+
+ if (!skip_error_logging)
+ {
+ trace::error(_X("realpath(%s) failed: %s"), path->c_str(), strerror(errno));
+ }
+
+ return false;
+ }
+
+ path->assign(resolved);
+ ::free(resolved);
+ return true;
+}
+
+bool pal::file_exists(const pal::string_t& path)
+{
+ return (::access(path.c_str(), F_OK) == 0);
+}
+
+static void readdir(const pal::string_t& path, const pal::string_t& pattern, bool onlydirectories, std::vector<pal::string_t>* list)
+{
+ assert(list != nullptr);
+
+ std::vector<pal::string_t>& files = *list;
+
+ auto dir = opendir(path.c_str());
+ if (dir != nullptr)
+ {
+ struct dirent* entry = nullptr;
+ while ((entry = readdir(dir)) != nullptr)
+ {
+ if (fnmatch(pattern.c_str(), entry->d_name, FNM_PATHNAME) != 0)
+ {
+ continue;
+ }
+
+ // We are interested in files only
+ switch (entry->d_type)
+ {
+ case DT_DIR:
+ break;
+
+ case DT_REG:
+ if (onlydirectories)
+ {
+ continue;
+ }
+ break;
+
+ // Handle symlinks and file systems that do not support d_type
+ case DT_LNK:
+ case DT_UNKNOWN:
+ {
+ struct stat sb;
+
+ if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1)
+ {
+ continue;
+ }
+
+ if (onlydirectories)
+ {
+ if (!S_ISDIR(sb.st_mode))
+ {
+ continue;
+ }
+ break;
+ }
+ else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode))
+ {
+ continue;
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+
+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+ {
+ continue;
+ }
+
+ files.emplace_back(entry->d_name);
+ }
+
+ closedir(dir);
+ }
+}
+
+void pal::readdir(const string_t& path, const string_t& pattern, std::vector<pal::string_t>* list)
+{
+ ::readdir(path, pattern, false, list);
+}
+
+void pal::readdir(const pal::string_t& path, std::vector<pal::string_t>* list)
+{
+ ::readdir(path, _X("*"), false, list);
+}
+
+void pal::readdir_onlydirectories(const pal::string_t& path, const string_t& pattern, std::vector<pal::string_t>* list)
+{
+ ::readdir(path, pattern, true, list);
+}
+
+void pal::readdir_onlydirectories(const pal::string_t& path, std::vector<pal::string_t>* list)
+{
+ ::readdir(path, _X("*"), true, list);
+}
+
+bool pal::is_running_in_wow64()
+{
+ return false;
+}
+
+bool pal::are_paths_equal_with_normalized_casing(const string_t& path1, const string_t& path2)
+{
+#if defined(__APPLE__)
+ // On Mac, paths are case-insensitive
+ return (strcasecmp(path1.c_str(), path2.c_str()) == 0);
+#else
+ // On Linux, paths are case-sensitive
+ return path1 == path2;
+#endif
+}