Chris Roberts c4084a6387 Extract smblist information based on position
Since we are no longer extracting information based on key value due
to localization issues, use start and end locations to extract data.
This prevents errors when extra information is included like Scope.
2018-08-30 10:58:56 -07:00

195 lines
7.0 KiB
Ruby

module VagrantPlugins
module HostWindows
module Cap
class SMB
# Number of seconds to display UAC warning to user
UAC_PROMPT_WAIT = 4
@@logger = Log4r::Logger.new("vagrant::host::windows::smb")
def self.smb_installed(env)
psv = Vagrant::Util::PowerShell.version.to_i
if psv < 3
return false
end
true
end
# Required options for mounting a share hosted on Windows
# NOTE: Windows deprecated smb 1.0 so a minimum of 2.0 must be enabled
def self.smb_mount_options(env)
["vers=2.0"]
end
def self.smb_validate_password(env, machine, username, password)
script_path = File.expand_path("../../scripts/check_credentials.ps1", __FILE__)
args = []
args << "-username" << "'#{username.gsub("'", "''")}'"
args << "-password" << "'#{password.gsub("'", "''")}'"
r = Vagrant::Util::PowerShell.execute(script_path, *args)
r.exit_code == 0
end
def self.smb_cleanup(env, machine, opts)
script_path = File.expand_path("../../scripts/unset_share.ps1", __FILE__)
m_id = machine_id(machine)
prune_shares = existing_shares.map do |share_name, share_info|
if share_info["Description"].to_s.start_with?("vgt-#{m_id}-")
@@logger.info("removing smb share name=#{share_name} id=#{m_id}")
share_name
else
@@logger.info("skipping smb share removal, not owned name=#{share_name}")
@@logger.debug("smb share ID not present name=#{share_name} id=#{m_id} description=#{share_info["Description"]}")
nil
end
end.compact
@@logger.debug("shares to be removed: #{prune_shares}")
if prune_shares.size > 0
machine.env.ui.warn("\n" + I18n.t("vagrant_sf_smb.uac.prune_warning") + "\n")
sleep UAC_PROMPT_WAIT
@@logger.info("remove shares: #{prune_shares}")
result = Vagrant::Util::PowerShell.execute(script_path, *prune_shares, sudo: true)
if result.exit_code != 0
failed_name = result.stdout.to_s.sub("share name: ", "")
raise SyncedFolderSMB::Errors::PruneShareFailed,
name: failed_name,
stderr: result.stderr,
stdout: result.stdout
end
end
end
def self.smb_prepare(env, machine, folders, opts)
script_path = File.expand_path("../../scripts/set_share.ps1", __FILE__)
shares = []
current_shares = existing_shares
folders.each do |id, data|
hostpath = data[:hostpath].to_s
chksum_id = Digest::MD5.hexdigest(id)
name = "vgt-#{machine_id(machine)}-#{chksum_id}"
data[:smb_id] ||= name
# Check if this name is already in use
if share_info = current_shares[data[:smb_id]]
exist_path = File.expand_path(share_info["Path"]).downcase
request_path = File.expand_path(hostpath).downcase
if !hostpath.empty? && exist_path != request_path
raise SyncedFolderSMB::Errors::SMBNameError,
path: hostpath,
existing_path: share_info["Path"],
name: data[:smb_id]
end
@@logger.info("skip creation of existing share name=#{name} id=#{data[:smb_id]}")
next
end
@@logger.info("creating new share name=#{name} id=#{data[:smb_id]}")
shares << [
"\"#{hostpath.gsub("/", "\\")}\"",
name,
data[:smb_id]
]
end
if !shares.empty?
machine.env.ui.warn("\n" + I18n.t("vagrant_sf_smb.uac.create_warning") + "\n")
sleep(UAC_PROMPT_WAIT)
result = Vagrant::Util::PowerShell.execute(script_path, *shares, sudo: true)
if result.exit_code != 0
share_path = result.stdout.to_s.sub("share path: ", "")
raise SyncedFolderSMB::Errors::DefineShareFailed,
host: share_path,
stderr: result.stderr,
stdout: result.stdout
end
end
end
# Generate a list of existing local smb shares
#
# @return [Hash]
def self.existing_shares
shares = get_smbshares || get_netshares
if shares.nil?
raise SyncedFolderSMB::Errors::SMBListFailed
end
@@logger.debug("local share listing: #{shares}")
shares
end
# Get current SMB share list using Get-SmbShare
#
# @return [Hash]
def self.get_smbshares
result = Vagrant::Util::PowerShell.execute_cmd("Get-SmbShare|Format-List|Out-String -Width 4096")
if result.nil?
return nil
end
share_data = result.strip.lines
shares = {}
name = nil
until share_data.empty?
content = share_data.take_while{|line| !line.strip.empty? }
share_name = content[0].strip.split(":", 2).last.strip
shares[share_name] = {
"Path" => content[-2].strip.split(":", 2).last.strip,
"Description" => content[-1].strip.split(":", 2).last.strip
}
share_data.slice!(0, content.length + 1)
end
shares
end
# Get current SMB share list using net.exe
#
# @return [Hash]
def self.get_netshares
result = Vagrant::Util::PowerShell.execute_cmd("net share | Out-String -Width 4096")
if result.nil?
return nil
end
share_data = result.strip.lines
# Remove header information
share_data.slice!(0, 2)
# Remove footer information
share_data.slice!(share_data.size - 1, share_data.size)
share_names = share_data.map do |line|
line.strip.split(/\s+/).first.strip
end
shares = {}
share_names.each do |share_name|
result = Vagrant::Util::PowerShell.execute_cmd("net share #{share_name} | Out-String -Width 4096")
next if result.nil?
result.strip!
share_info = result.lines
shares[share_name] = {
"Path" => share_info[1].split(/\s+/, 2).last.strip,
"Description" => share_info[2].split(/\s+/, 2).last.strip
}
end
shares
end
# Generates a unique identifier for the given machine
# based on the name, provider name, and working directory
# of the environment.
#
# @param [Vagrant::Machine] machine
# @return [String]
def self.machine_id(machine)
@@logger.debug("generating machine ID name=#{machine.name} cwd=#{machine.env.cwd}")
Digest::MD5.hexdigest("#{machine.name}-#{machine.provider_name}-#{machine.env.cwd}")
end
end
end
end
end