2017-12-16 00:31:44 +00:00
|
|
|
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
|
|
|
|
|
2018-04-26 15:45:05 +00:00
|
|
|
# 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
|
|
|
|
|
2018-01-13 00:43:19 +00:00
|
|
|
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
|
|
|
|
|
2017-12-16 00:31:44 +00:00
|
|
|
def self.smb_cleanup(env, machine, opts)
|
|
|
|
script_path = File.expand_path("../../scripts/unset_share.ps1", __FILE__)
|
|
|
|
|
|
|
|
m_id = machine_id(machine)
|
2018-01-02 22:03:54 +00:00
|
|
|
prune_shares = existing_shares.map do |share_name, share_info|
|
2018-06-11 19:58:35 +00:00
|
|
|
if share_info["Description"].to_s.start_with?("vgt-#{m_id}-")
|
2018-01-02 22:03:54 +00:00
|
|
|
@@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
|
2017-12-16 00:31:44 +00:00
|
|
|
end
|
|
|
|
end.compact
|
2018-01-02 22:03:54 +00:00
|
|
|
|
2017-12-16 00:31:44 +00:00
|
|
|
@@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 = []
|
2018-01-02 22:03:54 +00:00
|
|
|
current_shares = existing_shares
|
2017-12-16 00:31:44 +00:00
|
|
|
folders.each do |id, data|
|
2018-01-02 22:03:54 +00:00
|
|
|
hostpath = data[:hostpath].to_s
|
2017-12-16 00:31:44 +00:00
|
|
|
|
|
|
|
chksum_id = Digest::MD5.hexdigest(id)
|
|
|
|
name = "vgt-#{machine_id(machine)}-#{chksum_id}"
|
|
|
|
data[:smb_id] ||= name
|
|
|
|
|
2018-01-02 22:03:54 +00:00
|
|
|
# Check if this name is already in use
|
|
|
|
if share_info = current_shares[data[:smb_id]]
|
2018-01-03 18:05:05 +00:00
|
|
|
exist_path = File.expand_path(share_info["Path"]).downcase
|
|
|
|
request_path = File.expand_path(hostpath).downcase
|
|
|
|
if !hostpath.empty? && exist_path != request_path
|
2018-01-02 22:36:28 +00:00
|
|
|
raise SyncedFolderSMB::Errors::SMBNameError,
|
|
|
|
path: hostpath,
|
|
|
|
existing_path: share_info["Path"],
|
|
|
|
name: data[:smb_id]
|
2018-01-02 22:03:54 +00:00
|
|
|
end
|
|
|
|
@@logger.info("skip creation of existing share name=#{name} id=#{data[:smb_id]}")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2017-12-16 00:31:44 +00:00
|
|
|
@@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
|
|
|
|
|
2018-01-02 22:03:54 +00:00
|
|
|
# Generate a list of existing local smb shares
|
|
|
|
#
|
|
|
|
# @return [Hash]
|
|
|
|
def self.existing_shares
|
2018-05-04 21:07:33 +00:00
|
|
|
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
|
2018-06-12 22:36:14 +00:00
|
|
|
result = Vagrant::Util::PowerShell.execute_cmd("Get-SmbShare|Format-List|Out-String -Width 4096")
|
2018-01-02 22:03:54 +00:00
|
|
|
if result.nil?
|
2018-05-04 21:07:33 +00:00
|
|
|
return nil
|
2018-01-02 22:03:54 +00:00
|
|
|
end
|
2018-07-19 20:40:18 +00:00
|
|
|
share_data = result.strip.lines
|
2018-01-02 22:03:54 +00:00
|
|
|
shares = {}
|
|
|
|
name = nil
|
2018-07-19 20:40:18 +00:00
|
|
|
until share_data.empty?
|
|
|
|
content = share_data.take_while{|line| !line.strip.empty? }
|
|
|
|
if content.size != 3
|
|
|
|
@@logger.warn("expected SMB data check to be size 3 but was size #{content.size}")
|
|
|
|
@@logger.debug("unprocessed SMB data: #{content.inspect}")
|
|
|
|
next
|
2018-01-02 22:03:54 +00:00
|
|
|
end
|
2018-07-19 20:40:18 +00:00
|
|
|
share_name = content[0].strip.split(":", 2).last.strip
|
|
|
|
shares[share_name] = {
|
|
|
|
"Path" => content[1].strip.split(":", 2).last.strip,
|
|
|
|
"Description" => content[2].strip.split(":", 2).last.strip
|
|
|
|
}
|
|
|
|
share_data.slice!(0, content.length + 1)
|
2018-01-02 22:03:54 +00:00
|
|
|
end
|
2018-05-04 21:07:33 +00:00
|
|
|
shares
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get current SMB share list using net.exe
|
|
|
|
#
|
|
|
|
# @return [Hash]
|
|
|
|
def self.get_netshares
|
2018-06-12 22:36:14 +00:00
|
|
|
result = Vagrant::Util::PowerShell.execute_cmd("net share | Out-String -Width 4096")
|
2018-05-04 21:07:33 +00:00
|
|
|
if result.nil?
|
|
|
|
return nil
|
|
|
|
end
|
2018-07-19 20:40:18 +00:00
|
|
|
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
|
2018-05-04 21:07:33 +00:00
|
|
|
end
|
|
|
|
shares = {}
|
|
|
|
share_names.each do |share_name|
|
2018-06-12 22:36:14 +00:00
|
|
|
result = Vagrant::Util::PowerShell.execute_cmd("net share #{share_name} | Out-String -Width 4096")
|
2018-05-04 21:07:33 +00:00
|
|
|
next if result.nil?
|
2018-07-19 20:40:18 +00:00
|
|
|
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
|
|
|
|
}
|
2018-05-04 21:07:33 +00:00
|
|
|
end
|
2018-01-02 22:03:54 +00:00
|
|
|
shares
|
|
|
|
end
|
|
|
|
|
2017-12-16 00:31:44 +00:00
|
|
|
# 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
|