Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .expeditor/build_gems.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ $gem_path = [string]$temp.path + "\vendor\bundle\ruby\3.1.0"
[Environment]::SetEnvironmentVariable("GEM_PATH", $gem_path)
[Environment]::SetEnvironmentVariable("GEM_ROOT", $gem_path)
[Environment]::SetEnvironmentVariable("BUNDLE_GEMFILE", "$($temp.path)\Gemfile")
Write-Output "--- :gem: Installing uri into the isolated vendor bundle"
New-Item -ItemType Directory -Force -Path $gem_path | Out-Null
gem install uri --install-dir $gem_path --no-document
if (-not $?) { throw "unable to install uri gem" }
Write-Output "`r"

Write-Output "--- :put_litter_in_its_place: Removing any existing Chef PowerShell DLL's since they'll conflict with rspec"
Expand All @@ -205,7 +209,7 @@ hab pkg install chef/chef-infra-client/18.8.50/20251022232751 --channel "chef-ch
Write-Output "`r"

Write-Output "--- :point_right: finally verifying the gem code (cookstyle, spellcheck, spec)"
bundle update
bundle install
bundle exec rake gem_check
if (-not $?) { throw "Bundle Gem failed"}

Expand Down
2 changes: 1 addition & 1 deletion chef-powershell/chef_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,8 @@ wrlinux
Wrock
WSUS
xargs
xattr
XATTR
xattr
Xcode
xenenterprise
xenial
Expand Down
7 changes: 7 additions & 0 deletions chef-powershell/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
"chefdir",
"chefignored",
"chefignores",
"chefpath",
"CHEFPOWERSHELL",
"ChefPowerShell",
"chefscriptresult",
Expand All @@ -207,6 +208,7 @@
"chilcote",
"CHINESEBIG",
"Chouhan",
"cinst",
"ckbk",
"cksum",
"cleanpath",
Expand Down Expand Up @@ -239,6 +241,7 @@
"COMSPEC",
"comspec",
"concat",
"concrt",
"Cond",
"confd",
"confdef",
Expand Down Expand Up @@ -645,6 +648,7 @@
"homepath",
"HOMESHARE",
"Hongli",
"hostfxr",
"hostnamectl",
"Hostnames",
"hostnames",
Expand Down Expand Up @@ -845,6 +849,7 @@
"localmachine",
"localname",
"localport",
"localrepo",
"localsystem",
"localtime",
"LOCKCOUNT",
Expand Down Expand Up @@ -1350,6 +1355,7 @@
"REDIR",
"redirections",
"REDIRECTOR",
"redist",
"Redistributable",
"redownloading",
"Reenable",
Expand Down Expand Up @@ -1765,6 +1771,7 @@
"Vargo",
"variablevalue",
"Vasiliy",
"vccorlib",
"vcruntime",
"vendored",
"versio",
Expand Down
40 changes: 40 additions & 0 deletions chef-powershell/lib/chef-powershell/powershell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@
require_relative "unicode"

class ChefPowerShell
module Kernel32
extend FFI::Library
ffi_lib "kernel32"
attach_function :SetDllDirectoryA, %i{string}, :int
attach_function :SetDefaultDllDirectories, %i{uint32}, :int
attach_function :AddDllDirectory, %i{pointer}, :pointer

# https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000

# AddDllDirectory requires a wide (UTF-16LE), null-terminated string (LPCWSTR).
# Returns true if the directory was registered successfully.
def self.register_search_directory(path)
wide_path = (path + "\0").encode("UTF-16LE")
ptr = FFI::MemoryPointer.new(:uint8, wide_path.bytesize)
ptr.put_bytes(0, wide_path)
!(AddDllDirectory(ptr).address == 0)
end
end

class PowerShell

attr_reader :result
Expand Down Expand Up @@ -77,6 +97,26 @@ def initialize(script, timeout: -1)
# Bundle install ensures that the correct architecture binaries are installed into the path.
# @powershell_dll = Gem.loaded_specs["chef-powershell"].full_gem_path + "/bin/ruby_bin_folder/#{ENV["PROCESSOR_ARCHITECTURE"]}/Chef.PowerShell.Wrapper.dll"
@powershell_dll = self.class.resolve_wrapper_dll

# Windows' default LoadLibrary(Ex) search order used by FFI does NOT
# include the directory of the DLL being loaded -- FFI loads on Windows
# with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which only searches the hosting
# EXE's directory, System32, and directories registered via
# SetDllDirectory/AddDllDirectory (notably *not* PATH or the current
# directory). Chef.PowerShell.Wrapper.dll depends on native VC++ runtime
# DLLs (vcruntime140.dll, msvcp140.dll, etc.) that live alongside it in
# bin_dir, so that directory must be registered explicitly or those
# dependent DLLs will fail to resolve (error 126) even though the
# wrapper DLL itself loads fine via its absolute path.
bin_dir = File.dirname(@powershell_dll)
Kernel32.SetDefaultDllDirectories(Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
raise LoadError, "Failed to register DLL search directory: #{bin_dir}" unless Kernel32.register_search_directory(bin_dir)

# Retained alongside AddDllDirectory for defense in depth / older Windows
# compatibility (AddDllDirectory requires Windows 8+/Server 2012+, or
# Windows 7 SP1/Server 2008 R2 SP1 with KB2533623).
Kernel32.SetDllDirectoryA(bin_dir)

exec(script, timeout: timeout)
end

Expand Down
31 changes: 31 additions & 0 deletions chef-powershell/lib/chef-powershell/pwsh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,37 @@ def exec(script, timeout: -1)
end

@powershell_dll = self.class.resolve_core_wrapper_dll

# Windows' default LoadLibrary(Ex) search order used by FFI does NOT
# include the directory of the DLL being loaded -- FFI loads on Windows
# with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which only searches the hosting
# EXE's directory, System32, and directories registered via
# SetDllDirectory/AddDllDirectory (notably *not* PATH or the current
# directory).
#
# Chef.PowerShell.Wrapper.Core.dll lives in a "shared/Microsoft.NETCore.App"
# folder separate from ruby.exe. It also depends on native VC++ runtime DLLs
# (vcruntime140.dll, msvcp140.dll, concrt140.dll, vccorlib140.dll) that are
# NOT copied into that nested folder -- they only exist in the flat
# "ruby_bin_folder/<arch>" directory alongside the .NET Framework wrapper.
# We must register BOTH directories explicitly. Do not assume System32
# already has the VC++ redistributable installed -- that is incidental to
# any given machine and must never be relied upon.
core_dir = File.dirname(@powershell_dll)
native_deps_dir = File.expand_path(File.join(core_dir, "..", "..", ".."))

Kernel32.SetDefaultDllDirectories(Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
[native_deps_dir, core_dir].each do |dir|
next if Kernel32.register_search_directory(dir)

raise LoadError, "Failed to register DLL search directory: #{dir}"
end

# Retained alongside AddDllDirectory for defense in depth / older Windows
# compatibility (AddDllDirectory requires Windows 8+/Server 2012+, or
# Windows 7 SP1/Server 2008 R2 SP1 with KB2533623).
Kernel32.SetDllDirectoryA(core_dir)

super
ensure
ENV["DOTNET_MULTILEVEL_LOOKUP"] = original_dml
Expand Down
Loading