pdk 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/lib/pdk/cli/validate.rb +2 -1
- data/lib/pdk/module/build.rb +12 -12
- data/lib/pdk/module/metadata.rb +1 -1
- data/lib/pdk/util.rb +4 -1
- data/lib/pdk/util/puppet_version.rb +1 -1
- data/lib/pdk/util/ruby_version.rb +7 -0
- data/lib/pdk/util/windows.rb +10 -3
- data/lib/pdk/util/windows/api_types.rb +57 -0
- data/lib/pdk/util/windows/file.rb +36 -0
- data/lib/{puppet → pdk}/util/windows/string.rb +2 -2
- data/lib/pdk/version.rb +1 -1
- data/locales/pdk.pot +21 -33
- metadata +5 -6
- data/lib/puppet/util/windows.rb +0 -14
- data/lib/puppet/util/windows/api_types.rb +0 -282
- data/lib/puppet/util/windows/file.rb +0 -488
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08eefaca77036460baaf59107c23f00582b61244'
|
4
|
+
data.tar.gz: '078a7e34d073f5e9571642188d4983d046284fa1'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89d63ff36f12e738d768c1fc592fd6bc8835ff0dd4306f9d5ea10a6cf285d532e3bff21c1075420c7114f557e5fb713874615e13a1749a653c225a945aea1bac
|
7
|
+
data.tar.gz: 58ebcc413564a0866efc92af272b0d7fe26e5f961a9b7625be38c75969a4f2307b8e9339b564916aa9cd3beda8849b93016921c6ba491192e9d0932cb583ef29
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,27 @@ All changes to this repo will be documented in this file.
|
|
4
4
|
See the [release notes](https://puppet.com/docs/pdk/latest/release_notes.html) for a high-level summary.
|
5
5
|
|
6
6
|
|
7
|
-
## [v1.7.
|
7
|
+
## [v1.7.1](https://github.com/puppetlabs/pdk/tree/v1.7.1) (2018-10-05)
|
8
|
+
[Full Changelog](https://github.com/puppetlabs/pdk/compare/v1.7.0...v1.7.1)
|
9
|
+
|
10
|
+
**Implemented enhancements:**
|
11
|
+
|
12
|
+
- \(PDK-1100\) Exclude known artifacts from build instead of cleaning [\#575](https://github.com/puppetlabs/pdk/pull/575) ([rodjek](https://github.com/rodjek))
|
13
|
+
- \(PDK-1056\) Adds support for Ruby 2.5.1 in packaged PDK version [\#568](https://github.com/puppetlabs/pdk/pull/568) ([bmjen](https://github.com/bmjen))
|
14
|
+
- \(PDK-1099\) Merge Puppet::Util::Windows into PDK namespace [\#565](https://github.com/puppetlabs/pdk/pull/565) ([rodjek](https://github.com/rodjek))
|
15
|
+
|
16
|
+
**Fixed bugs:**
|
17
|
+
|
18
|
+
- \(PDK-1181\) Display error when metadata.json missing or unreadable [\#574](https://github.com/puppetlabs/pdk/pull/574) ([rodjek](https://github.com/rodjek))
|
19
|
+
- \(PDK-1173\) Update pdk validate help output for powershell [\#573](https://github.com/puppetlabs/pdk/pull/573) ([rodjek](https://github.com/rodjek))
|
20
|
+
|
21
|
+
**Merged pull requests:**
|
22
|
+
|
23
|
+
- \(maint\) Update PDK metadata defaults to include Puppet 6 [\#572](https://github.com/puppetlabs/pdk/pull/572) ([bmjen](https://github.com/bmjen))
|
24
|
+
- \(maint\) Update package tests for ruby 2.5.1 as the new default [\#571](https://github.com/puppetlabs/pdk/pull/571) ([bmjen](https://github.com/bmjen))
|
25
|
+
- Bump version to 1.8.0.pre [\#564](https://github.com/puppetlabs/pdk/pull/564) ([bmjen](https://github.com/bmjen))
|
26
|
+
|
27
|
+
## [v1.7.0](https://github.com/puppetlabs/pdk/tree/v1.7.0) (2018-08-15)
|
8
28
|
[Full Changelog](https://github.com/puppetlabs/pdk/compare/v1.6.1...v1.7.0)
|
9
29
|
|
10
30
|
**Implemented enhancements:**
|
@@ -17,6 +37,7 @@ See the [release notes](https://puppet.com/docs/pdk/latest/release_notes.html) f
|
|
17
37
|
|
18
38
|
**Merged pull requests:**
|
19
39
|
|
40
|
+
- Release 1.7.0 [\#563](https://github.com/puppetlabs/pdk/pull/563) ([bmjen](https://github.com/bmjen))
|
20
41
|
- \(maint\) Fix package tests [\#562](https://github.com/puppetlabs/pdk/pull/562) ([bmjen](https://github.com/bmjen))
|
21
42
|
- \(PDK-1083\) Bump PDK version to 1.7.0.pre [\#556](https://github.com/puppetlabs/pdk/pull/556) ([rodjek](https://github.com/rodjek))
|
22
43
|
- \(PDK-1077\) Expand the package acceptance test suite [\#554](https://github.com/puppetlabs/pdk/pull/554) ([rodjek](https://github.com/rodjek))
|
data/lib/pdk/cli/validate.rb
CHANGED
@@ -8,7 +8,8 @@ module PDK::CLI
|
|
8
8
|
description _(
|
9
9
|
"Run metadata, Puppet, or Ruby validation.\n\n" \
|
10
10
|
'[validators] is an optional comma-separated list of validators to use. ' \
|
11
|
-
|
11
|
+
'If not specified, all validators are used. ' \
|
12
|
+
"Note that when using PowerShell, the list of validators must be enclosed in single quotes.\n\n" \
|
12
13
|
'[targets] is an optional space-separated list of files or directories to be validated. ' \
|
13
14
|
'If not specified, validators are run against all applicable files in the module.',
|
14
15
|
)
|
data/lib/pdk/module/build.rb
CHANGED
@@ -8,6 +8,16 @@ require 'pdk/tests/unit'
|
|
8
8
|
module PDK
|
9
9
|
module Module
|
10
10
|
class Build
|
11
|
+
DEFAULT_IGNORED = [
|
12
|
+
'/pkg/',
|
13
|
+
'.*',
|
14
|
+
'~*',
|
15
|
+
'/coverage',
|
16
|
+
'/checksums.json',
|
17
|
+
'/REVISION',
|
18
|
+
'/spec/fixtures/modules/',
|
19
|
+
].freeze
|
20
|
+
|
11
21
|
def self.invoke(options = {})
|
12
22
|
new(options).build
|
13
23
|
end
|
@@ -37,7 +47,6 @@ module PDK
|
|
37
47
|
#
|
38
48
|
# @return [String] The path to the built package file.
|
39
49
|
def build
|
40
|
-
cleanup_module
|
41
50
|
create_build_dir
|
42
51
|
|
43
52
|
stage_module_in_build_dir
|
@@ -83,17 +92,6 @@ module PDK
|
|
83
92
|
FileUtils.rm_rf(build_dir, secure: true)
|
84
93
|
end
|
85
94
|
|
86
|
-
# Clean up any files created during use of the PDK that shouldn't be part
|
87
|
-
# of the built module (e.g. test fixtures).
|
88
|
-
#
|
89
|
-
# @return nil
|
90
|
-
def cleanup_module
|
91
|
-
PDK::Util::Bundler.ensure_bundle!
|
92
|
-
PDK::Util::Bundler.ensure_binstubs!('rake')
|
93
|
-
|
94
|
-
PDK::Test::Unit.tear_down
|
95
|
-
end
|
96
|
-
|
97
95
|
# Combine the module name and version into a Forge-compatible dash
|
98
96
|
# separated string.
|
99
97
|
#
|
@@ -215,6 +213,8 @@ module PDK
|
|
215
213
|
ignored = ignored.add("\/#{File.basename(target_dir)}\/")
|
216
214
|
end
|
217
215
|
|
216
|
+
DEFAULT_IGNORED.each { |r| ignored.add(r) }
|
217
|
+
|
218
218
|
ignored
|
219
219
|
end
|
220
220
|
end
|
data/lib/pdk/module/metadata.rb
CHANGED
data/lib/pdk/util.rb
CHANGED
@@ -58,7 +58,7 @@ module PDK
|
|
58
58
|
unless File.exist?(path)
|
59
59
|
raise PDK::CLI::FatalError, _("Cannot resolve a full path to '%{path}', as it does not currently exist.") % { path: path }
|
60
60
|
end
|
61
|
-
|
61
|
+
PDK::Util::Windows::File.get_long_pathname(path)
|
62
62
|
else
|
63
63
|
File.expand_path(path)
|
64
64
|
end
|
@@ -261,6 +261,9 @@ module PDK
|
|
261
261
|
else
|
262
262
|
nil
|
263
263
|
end
|
264
|
+
rescue ArgumentError => e
|
265
|
+
PDK.logger.error(e)
|
266
|
+
nil
|
264
267
|
end
|
265
268
|
module_function :module_pdk_version
|
266
269
|
end
|
@@ -45,7 +45,14 @@ module PDK
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def default_ruby_version
|
48
|
+
# For now, the packaged versions will be using default of 2.4.4.
|
49
|
+
return '2.4.4' if PDK::Util.package_install?
|
50
|
+
|
48
51
|
# TODO: may not be a safe assumption that highest available version should be default
|
52
|
+
latest_ruby_version
|
53
|
+
end
|
54
|
+
|
55
|
+
def latest_ruby_version
|
49
56
|
versions.keys.sort { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }.first
|
50
57
|
end
|
51
58
|
|
data/lib/pdk/util/windows.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
module
|
1
|
+
module PDK
|
2
2
|
module Util
|
3
|
+
module Windows
|
4
|
+
module File; end
|
5
|
+
|
6
|
+
if Gem.win_platform?
|
7
|
+
require 'pdk/util/windows/api_types'
|
8
|
+
require 'pdk/util/windows/string'
|
9
|
+
require 'pdk/util/windows/file'
|
10
|
+
end
|
11
|
+
end
|
3
12
|
end
|
4
13
|
end
|
5
|
-
|
6
|
-
require 'puppet/util/windows'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'pdk/util/windows/string'
|
3
|
+
|
4
|
+
module PDK::Util::Windows::APITypes
|
5
|
+
module ::FFI
|
6
|
+
WIN32_FALSE = 0
|
7
|
+
|
8
|
+
# standard Win32 error codes
|
9
|
+
ERROR_SUCCESS = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
class ::FFI::Pointer
|
13
|
+
def self.from_string_to_wide_string(str, &_block)
|
14
|
+
str = PDK::Util::Windows::String.wide_string(str)
|
15
|
+
FFI::MemoryPointer.new(:byte, str.bytesize) do |ptr|
|
16
|
+
# uchar here is synonymous with byte
|
17
|
+
ptr.put_array_of_uchar(0, str.bytes.to_a)
|
18
|
+
|
19
|
+
yield ptr
|
20
|
+
end
|
21
|
+
|
22
|
+
# ptr has already had free called, so nothing to return
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_wide_string(char_length, dst_encoding = Encoding::UTF_8, encode_options = {})
|
27
|
+
# char_length is number of wide chars (typically excluding NULLs), *not* bytes
|
28
|
+
str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')
|
29
|
+
str.encode(dst_encoding, str.encoding, encode_options)
|
30
|
+
rescue StandardError => e
|
31
|
+
PDK.logger.debug _('Unable to convert value %{string} to encoding %{encoding} due to %{error}') % {
|
32
|
+
string: str.dump,
|
33
|
+
encoding: dst_encoding,
|
34
|
+
error: e.inspect,
|
35
|
+
}
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# FFI Types
|
41
|
+
# https://github.com/ffi/ffi/wiki/Types
|
42
|
+
|
43
|
+
# Windows - Common Data Types
|
44
|
+
# https://msdn.microsoft.com/en-us/library/cc230309.aspx
|
45
|
+
|
46
|
+
# Windows Data Types
|
47
|
+
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
|
48
|
+
|
49
|
+
FFI.typedef :uint32, :dword
|
50
|
+
# buffer_inout is similar to pointer (platform specific), but optimized for buffers
|
51
|
+
FFI.typedef :buffer_inout, :lpwstr
|
52
|
+
# buffer_in is similar to pointer (platform specific), but optimized for CONST read only buffers
|
53
|
+
FFI.typedef :buffer_in, :lpcwstr
|
54
|
+
# 8 bits per byte
|
55
|
+
FFI.typedef :uchar, :byte
|
56
|
+
FFI.typedef :uint16, :wchar
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pdk/util/windows'
|
2
|
+
|
3
|
+
module PDK::Util::Windows::File
|
4
|
+
require 'ffi'
|
5
|
+
extend FFI::Library
|
6
|
+
extend PDK::Util::Windows::String
|
7
|
+
|
8
|
+
def get_long_pathname(path)
|
9
|
+
converted = ''
|
10
|
+
FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
|
11
|
+
# includes terminating NULL
|
12
|
+
buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)
|
13
|
+
FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
|
14
|
+
if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
|
15
|
+
raise _('Failed to call GetLongPathName')
|
16
|
+
end
|
17
|
+
|
18
|
+
converted = converted_ptr.read_wide_string(buffer_size - 1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
converted
|
23
|
+
end
|
24
|
+
module_function :get_long_pathname
|
25
|
+
|
26
|
+
ffi_convention :stdcall
|
27
|
+
|
28
|
+
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
|
29
|
+
# DWORD WINAPI GetLongPathName(
|
30
|
+
# _In_ LPCTSTR lpszShortPath,
|
31
|
+
# _Out_ LPTSTR lpszLongPath,
|
32
|
+
# _In_ DWORD cchBuffer
|
33
|
+
# );
|
34
|
+
ffi_lib :kernel32
|
35
|
+
attach_function :GetLongPathNameW, [:lpcwstr, :lpwstr, :dword], :dword
|
36
|
+
end
|
data/lib/pdk/version.rb
CHANGED
data/locales/pdk.pot
CHANGED
@@ -6,11 +6,11 @@
|
|
6
6
|
#, fuzzy
|
7
7
|
msgid ""
|
8
8
|
msgstr ""
|
9
|
-
"Project-Id-Version: puppet development kit v1.
|
9
|
+
"Project-Id-Version: puppet development kit v1.7.0-16-g810d1d3be\n"
|
10
10
|
"\n"
|
11
11
|
"Report-Msgid-Bugs-To: docs@puppet.com\n"
|
12
|
-
"POT-Creation-Date: 2018-
|
13
|
-
"PO-Revision-Date: 2018-
|
12
|
+
"POT-Creation-Date: 2018-10-05 11:07-0700\n"
|
13
|
+
"PO-Revision-Date: 2018-10-05 11:07-0700\n"
|
14
14
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
15
15
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
16
16
|
"Language: \n"
|
@@ -531,40 +531,40 @@ msgstr ""
|
|
531
531
|
msgid ""
|
532
532
|
"Run metadata, Puppet, or Ruby validation.\n"
|
533
533
|
"\n"
|
534
|
-
"[validators] is an optional comma-separated list of validators to use. If not specified, all validators are used.\n"
|
534
|
+
"[validators] is an optional comma-separated list of validators to use. If not specified, all validators are used. Note that when using PowerShell, the list of validators must be enclosed in single quotes.\n"
|
535
535
|
"\n"
|
536
536
|
"[targets] is an optional space-separated list of files or directories to be validated. If not specified, validators are run against all applicable files in the module."
|
537
537
|
msgstr ""
|
538
538
|
|
539
|
-
#: ../lib/pdk/cli/validate.rb:
|
539
|
+
#: ../lib/pdk/cli/validate.rb:19
|
540
540
|
msgid "List all available validators."
|
541
541
|
msgstr ""
|
542
542
|
|
543
|
-
#: ../lib/pdk/cli/validate.rb:
|
543
|
+
#: ../lib/pdk/cli/validate.rb:20
|
544
544
|
msgid "Automatically correct problems where possible."
|
545
545
|
msgstr ""
|
546
546
|
|
547
|
-
#: ../lib/pdk/cli/validate.rb:
|
547
|
+
#: ../lib/pdk/cli/validate.rb:21
|
548
548
|
msgid "Run validations in parallel."
|
549
549
|
msgstr ""
|
550
550
|
|
551
|
-
#: ../lib/pdk/cli/validate.rb:
|
551
|
+
#: ../lib/pdk/cli/validate.rb:34
|
552
552
|
msgid "Available validators: %{validator_names}"
|
553
553
|
msgstr ""
|
554
554
|
|
555
|
-
#: ../lib/pdk/cli/validate.rb:
|
555
|
+
#: ../lib/pdk/cli/validate.rb:41
|
556
556
|
msgid "Code validation can only be run from inside a valid module directory"
|
557
557
|
msgstr ""
|
558
558
|
|
559
|
-
#: ../lib/pdk/cli/validate.rb:
|
559
|
+
#: ../lib/pdk/cli/validate.rb:57
|
560
560
|
msgid "Unknown validator '%{v}'. Available validators: %{validators}."
|
561
561
|
msgstr ""
|
562
562
|
|
563
|
-
#: ../lib/pdk/cli/validate.rb:
|
563
|
+
#: ../lib/pdk/cli/validate.rb:67 ../lib/pdk/cli/validate.rb:71
|
564
564
|
msgid "Running all available validators..."
|
565
565
|
msgstr ""
|
566
566
|
|
567
|
-
#: ../lib/pdk/cli/validate.rb:
|
567
|
+
#: ../lib/pdk/cli/validate.rb:98
|
568
568
|
msgid "Validating module using %{num_of_threads} threads"
|
569
569
|
msgstr ""
|
570
570
|
|
@@ -798,7 +798,7 @@ msgstr ""
|
|
798
798
|
msgid "A task named '%{name}' already exists in this module; defined in %{file}"
|
799
799
|
msgstr ""
|
800
800
|
|
801
|
-
#: ../lib/pdk/module/build.rb:
|
801
|
+
#: ../lib/pdk/module/build.rb:158
|
802
802
|
msgid "Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}."
|
803
803
|
msgstr ""
|
804
804
|
|
@@ -1198,6 +1198,14 @@ msgstr ""
|
|
1198
1198
|
msgid "Unable to download %{url}. Check internet connectivity and try again. %{error}"
|
1199
1199
|
msgstr ""
|
1200
1200
|
|
1201
|
+
#: ../lib/pdk/util/windows/api_types.rb:31
|
1202
|
+
msgid "Unable to convert value %{string} to encoding %{encoding} due to %{error}"
|
1203
|
+
msgstr ""
|
1204
|
+
|
1205
|
+
#: ../lib/pdk/util/windows/file.rb:15
|
1206
|
+
msgid "Failed to call GetLongPathName"
|
1207
|
+
msgstr ""
|
1208
|
+
|
1201
1209
|
#: ../lib/pdk/validate/base_validator.rb:83
|
1202
1210
|
msgid "Invoking %{cmd}"
|
1203
1211
|
msgstr ""
|
@@ -1257,23 +1265,3 @@ msgstr ""
|
|
1257
1265
|
#: ../lib/pdk/validate/ruby/rubocop.rb:24
|
1258
1266
|
msgid "Checking Ruby code style (%{pattern})."
|
1259
1267
|
msgstr ""
|
1260
|
-
|
1261
|
-
#: ../lib/puppet/util/windows/api_types.rb:72
|
1262
|
-
msgid "Unable to read wide strings with %{null_terminator} terminal nulls"
|
1263
|
-
msgstr ""
|
1264
|
-
|
1265
|
-
#: ../lib/puppet/util/windows/api_types.rb:203
|
1266
|
-
msgid "Bad GUID format."
|
1267
|
-
msgstr ""
|
1268
|
-
|
1269
|
-
#: ../lib/puppet/util/windows/file.rb:158
|
1270
|
-
msgid "Failed to set file attributes"
|
1271
|
-
msgstr ""
|
1272
|
-
|
1273
|
-
#: ../lib/puppet/util/windows/file.rb:193
|
1274
|
-
msgid "out_buffer is required"
|
1275
|
-
msgstr ""
|
1276
|
-
|
1277
|
-
#: ../lib/puppet/util/windows/file.rb:274
|
1278
|
-
msgid "Failed to call GetLongPathName"
|
1279
|
-
msgstr ""
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08
|
11
|
+
date: 2018-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -258,6 +258,9 @@ files:
|
|
258
258
|
- lib/pdk/util/vendored_file.rb
|
259
259
|
- lib/pdk/util/version.rb
|
260
260
|
- lib/pdk/util/windows.rb
|
261
|
+
- lib/pdk/util/windows/api_types.rb
|
262
|
+
- lib/pdk/util/windows/file.rb
|
263
|
+
- lib/pdk/util/windows/string.rb
|
261
264
|
- lib/pdk/validate.rb
|
262
265
|
- lib/pdk/validate/base_validator.rb
|
263
266
|
- lib/pdk/validate/metadata/metadata_json_lint.rb
|
@@ -270,10 +273,6 @@ files:
|
|
270
273
|
- lib/pdk/validate/ruby/rubocop.rb
|
271
274
|
- lib/pdk/validate/ruby_validator.rb
|
272
275
|
- lib/pdk/version.rb
|
273
|
-
- lib/puppet/util/windows.rb
|
274
|
-
- lib/puppet/util/windows/api_types.rb
|
275
|
-
- lib/puppet/util/windows/file.rb
|
276
|
-
- lib/puppet/util/windows/string.rb
|
277
276
|
- locales/config.yaml
|
278
277
|
- locales/pdk.pot
|
279
278
|
homepage: https://github.com/puppetlabs/pdk
|
data/lib/puppet/util/windows.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
module Puppet
|
2
|
-
module Util
|
3
|
-
module Windows
|
4
|
-
module File; end
|
5
|
-
|
6
|
-
if Gem.win_platform?
|
7
|
-
# these reference platform specific gems
|
8
|
-
require 'puppet/util/windows/api_types'
|
9
|
-
require 'puppet/util/windows/string'
|
10
|
-
require 'puppet/util/windows/file'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,282 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
require 'puppet/util/windows/string'
|
3
|
-
|
4
|
-
module Puppet::Util::Windows::APITypes
|
5
|
-
module ::FFI
|
6
|
-
WIN32_FALSE = 0
|
7
|
-
|
8
|
-
# standard Win32 error codes
|
9
|
-
ERROR_SUCCESS = 0
|
10
|
-
end
|
11
|
-
|
12
|
-
module ::FFI::Library
|
13
|
-
# Wrapper method for attach_function + private
|
14
|
-
def attach_function_private(*args)
|
15
|
-
attach_function(*args)
|
16
|
-
private args[0]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ::FFI::Pointer
|
21
|
-
NULL_HANDLE = 0
|
22
|
-
|
23
|
-
def self.from_string_to_wide_string(str, &block)
|
24
|
-
str = Puppet::Util::Windows::String.wide_string(str)
|
25
|
-
FFI::MemoryPointer.new(:byte, str.bytesize) do |ptr|
|
26
|
-
# uchar here is synonymous with byte
|
27
|
-
ptr.put_array_of_uchar(0, str.bytes.to_a)
|
28
|
-
|
29
|
-
yield ptr
|
30
|
-
end
|
31
|
-
|
32
|
-
# ptr has already had free called, so nothing to return
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
def read_win32_bool
|
37
|
-
# BOOL is always a 32-bit integer in Win32
|
38
|
-
# some Win32 APIs return 1 for true, while others are non-0
|
39
|
-
read_int32 != FFI::WIN32_FALSE
|
40
|
-
end
|
41
|
-
|
42
|
-
alias_method :read_dword, :read_uint32
|
43
|
-
alias_method :read_win32_ulong, :read_uint32
|
44
|
-
alias_method :read_qword, :read_uint64
|
45
|
-
|
46
|
-
alias_method :read_hresult, :read_int32
|
47
|
-
|
48
|
-
def read_handle
|
49
|
-
type_size == 4 ? read_uint32 : read_uint64
|
50
|
-
end
|
51
|
-
|
52
|
-
alias_method :read_wchar, :read_uint16
|
53
|
-
alias_method :read_word, :read_uint16
|
54
|
-
alias_method :read_array_of_wchar, :read_array_of_uint16
|
55
|
-
|
56
|
-
def read_wide_string(char_length, dst_encoding = Encoding::UTF_8, encode_options = {})
|
57
|
-
# char_length is number of wide chars (typically excluding NULLs), *not* bytes
|
58
|
-
str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')
|
59
|
-
str.encode(dst_encoding, str.encoding, encode_options)
|
60
|
-
rescue Exception => e
|
61
|
-
Puppet.debug "Unable to convert value #{str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
|
62
|
-
raise
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param max_char_length [Integer] Maximum number of wide chars to return (typically excluding NULLs), *not* bytes
|
66
|
-
# @param null_terminator [Symbol] Number of number of null wchar characters, *not* bytes, that determine the end of the string
|
67
|
-
# null_terminator = :single_null, then the terminating sequence is two bytes of zero. This is UNIT16 = 0
|
68
|
-
# null_terminator = :double_null, then the terminating sequence is four bytes of zero. This is UNIT32 = 0
|
69
|
-
# @param encode_options [Hash] Accepts the same option hash that may be passed to String#encode in Ruby
|
70
|
-
def read_arbitrary_wide_string_up_to(max_char_length = 512, null_terminator = :single_null, encode_options = {})
|
71
|
-
if null_terminator != :single_null && null_terminator != :double_null
|
72
|
-
raise _("Unable to read wide strings with %{null_terminator} terminal nulls") % { null_terminator: null_terminator }
|
73
|
-
end
|
74
|
-
|
75
|
-
terminator_width = null_terminator == :single_null ? 1 : 2
|
76
|
-
reader_method = null_terminator == :single_null ? :get_uint16 : :get_uint32
|
77
|
-
|
78
|
-
# Look for a null terminating characters; if found, read up to that null (exclusive)
|
79
|
-
(0...max_char_length - terminator_width).each do |i|
|
80
|
-
return read_wide_string(i, Encoding::UTF_8, encode_options) if send(reader_method, (i * 2)) == 0
|
81
|
-
end
|
82
|
-
|
83
|
-
# String is longer than the max; read just to the max
|
84
|
-
read_wide_string(max_char_length, Encoding::UTF_8, encode_options)
|
85
|
-
end
|
86
|
-
|
87
|
-
def read_win32_local_pointer(&block)
|
88
|
-
ptr = nil
|
89
|
-
begin
|
90
|
-
ptr = read_pointer
|
91
|
-
yield ptr
|
92
|
-
ensure
|
93
|
-
if ptr && ! ptr.null?
|
94
|
-
if FFI::WIN32::LocalFree(ptr.address) != FFI::Pointer::NULL_HANDLE
|
95
|
-
Puppet.debug "LocalFree memory leak"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# ptr has already had LocalFree called, so nothing to return
|
101
|
-
nil
|
102
|
-
end
|
103
|
-
|
104
|
-
def read_com_memory_pointer(&block)
|
105
|
-
ptr = nil
|
106
|
-
begin
|
107
|
-
ptr = read_pointer
|
108
|
-
yield ptr
|
109
|
-
ensure
|
110
|
-
FFI::WIN32::CoTaskMemFree(ptr) if ptr && ! ptr.null?
|
111
|
-
end
|
112
|
-
|
113
|
-
# ptr has already had CoTaskMemFree called, so nothing to return
|
114
|
-
nil
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
alias_method :write_dword, :write_uint32
|
119
|
-
alias_method :write_word, :write_uint16
|
120
|
-
end
|
121
|
-
|
122
|
-
# FFI Types
|
123
|
-
# https://github.com/ffi/ffi/wiki/Types
|
124
|
-
|
125
|
-
# Windows - Common Data Types
|
126
|
-
# https://msdn.microsoft.com/en-us/library/cc230309.aspx
|
127
|
-
|
128
|
-
# Windows Data Types
|
129
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
|
130
|
-
|
131
|
-
FFI.typedef :uint16, :word
|
132
|
-
FFI.typedef :uint32, :dword
|
133
|
-
# uintptr_t is defined in an FFI conf as platform specific, either
|
134
|
-
# ulong_long on x64 or just ulong on x86
|
135
|
-
FFI.typedef :uintptr_t, :handle
|
136
|
-
FFI.typedef :uintptr_t, :hwnd
|
137
|
-
|
138
|
-
# buffer_inout is similar to pointer (platform specific), but optimized for buffers
|
139
|
-
FFI.typedef :buffer_inout, :lpwstr
|
140
|
-
# buffer_in is similar to pointer (platform specific), but optimized for CONST read only buffers
|
141
|
-
FFI.typedef :buffer_in, :lpcwstr
|
142
|
-
FFI.typedef :buffer_in, :lpcolestr
|
143
|
-
|
144
|
-
# string is also similar to pointer, but should be used for const char *
|
145
|
-
# NOTE that this is not wide, useful only for A suffixed functions
|
146
|
-
FFI.typedef :string, :lpcstr
|
147
|
-
|
148
|
-
# pointer in FFI is platform specific
|
149
|
-
# NOTE: for API calls with reserved lpvoid parameters, pass a FFI::Pointer::NULL
|
150
|
-
FFI.typedef :pointer, :lpcvoid
|
151
|
-
FFI.typedef :pointer, :lpvoid
|
152
|
-
FFI.typedef :pointer, :lpword
|
153
|
-
FFI.typedef :pointer, :lpbyte
|
154
|
-
FFI.typedef :pointer, :lpdword
|
155
|
-
FFI.typedef :pointer, :pdword
|
156
|
-
FFI.typedef :pointer, :phandle
|
157
|
-
FFI.typedef :pointer, :ulong_ptr
|
158
|
-
FFI.typedef :pointer, :pbool
|
159
|
-
FFI.typedef :pointer, :lpunknown
|
160
|
-
|
161
|
-
# any time LONG / ULONG is in a win32 API definition DO NOT USE platform specific width
|
162
|
-
# which is what FFI uses by default
|
163
|
-
# instead create new aliases for these very special cases
|
164
|
-
# NOTE: not a good idea to redefine FFI :ulong since other typedefs may rely on it
|
165
|
-
FFI.typedef :uint32, :win32_ulong
|
166
|
-
FFI.typedef :int32, :win32_long
|
167
|
-
# FFI bool can be only 1 byte at times,
|
168
|
-
# Win32 BOOL is a signed int, and is always 4 bytes, even on x64
|
169
|
-
# https://blogs.msdn.com/b/oldnewthing/archive/2011/03/28/10146459.aspx
|
170
|
-
FFI.typedef :int32, :win32_bool
|
171
|
-
|
172
|
-
# BOOLEAN (unlike BOOL) is a BYTE - typedef unsigned char BYTE;
|
173
|
-
FFI.typedef :uchar, :boolean
|
174
|
-
|
175
|
-
# Same as a LONG, a 32-bit signed integer
|
176
|
-
FFI.typedef :int32, :hresult
|
177
|
-
|
178
|
-
# NOTE: FFI already defines (u)short as a 16-bit (un)signed like this:
|
179
|
-
# FFI.typedef :uint16, :ushort
|
180
|
-
# FFI.typedef :int16, :short
|
181
|
-
|
182
|
-
# 8 bits per byte
|
183
|
-
FFI.typedef :uchar, :byte
|
184
|
-
FFI.typedef :uint16, :wchar
|
185
|
-
|
186
|
-
module ::FFI::WIN32
|
187
|
-
extend ::FFI::Library
|
188
|
-
|
189
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931(v=vs.85).aspx
|
190
|
-
# typedef struct _GUID {
|
191
|
-
# DWORD Data1;
|
192
|
-
# WORD Data2;
|
193
|
-
# WORD Data3;
|
194
|
-
# BYTE Data4[8];
|
195
|
-
# } GUID;
|
196
|
-
class GUID < FFI::Struct
|
197
|
-
layout :Data1, :dword,
|
198
|
-
:Data2, :word,
|
199
|
-
:Data3, :word,
|
200
|
-
:Data4, [:byte, 8]
|
201
|
-
|
202
|
-
def self.[](s)
|
203
|
-
raise _('Bad GUID format.') unless s =~ /^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/i
|
204
|
-
|
205
|
-
new.tap do |guid|
|
206
|
-
guid[:Data1] = s[0, 8].to_i(16)
|
207
|
-
guid[:Data2] = s[9, 4].to_i(16)
|
208
|
-
guid[:Data3] = s[14, 4].to_i(16)
|
209
|
-
guid[:Data4][0] = s[19, 2].to_i(16)
|
210
|
-
guid[:Data4][1] = s[21, 2].to_i(16)
|
211
|
-
s[24, 12].split('').each_slice(2).with_index do |a, i|
|
212
|
-
guid[:Data4][i + 2] = a.join('').to_i(16)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def ==(other) Windows.memcmp(other, self, size) == 0 end
|
218
|
-
end
|
219
|
-
|
220
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
|
221
|
-
# typedef struct _SYSTEMTIME {
|
222
|
-
# WORD wYear;
|
223
|
-
# WORD wMonth;
|
224
|
-
# WORD wDayOfWeek;
|
225
|
-
# WORD wDay;
|
226
|
-
# WORD wHour;
|
227
|
-
# WORD wMinute;
|
228
|
-
# WORD wSecond;
|
229
|
-
# WORD wMilliseconds;
|
230
|
-
# } SYSTEMTIME, *PSYSTEMTIME;
|
231
|
-
class SYSTEMTIME < FFI::Struct
|
232
|
-
layout :wYear, :word,
|
233
|
-
:wMonth, :word,
|
234
|
-
:wDayOfWeek, :word,
|
235
|
-
:wDay, :word,
|
236
|
-
:wHour, :word,
|
237
|
-
:wMinute, :word,
|
238
|
-
:wSecond, :word,
|
239
|
-
:wMilliseconds, :word
|
240
|
-
|
241
|
-
def to_local_time
|
242
|
-
Time.local(self[:wYear], self[:wMonth], self[:wDay],
|
243
|
-
self[:wHour], self[:wMinute], self[:wSecond], self[:wMilliseconds] * 1000)
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
|
248
|
-
# Contains a 64-bit value representing the number of 100-nanosecond
|
249
|
-
# intervals since January 1, 1601 (UTC).
|
250
|
-
# typedef struct _FILETIME {
|
251
|
-
# DWORD dwLowDateTime;
|
252
|
-
# DWORD dwHighDateTime;
|
253
|
-
# } FILETIME, *PFILETIME;
|
254
|
-
class FILETIME < FFI::Struct
|
255
|
-
layout :dwLowDateTime, :dword,
|
256
|
-
:dwHighDateTime, :dword
|
257
|
-
end
|
258
|
-
|
259
|
-
ffi_convention :stdcall
|
260
|
-
|
261
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa366730(v=vs.85).aspx
|
262
|
-
# HLOCAL WINAPI LocalFree(
|
263
|
-
# _In_ HLOCAL hMem
|
264
|
-
# );
|
265
|
-
ffi_lib :kernel32
|
266
|
-
attach_function :LocalFree, [:handle], :handle
|
267
|
-
|
268
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
|
269
|
-
# BOOL WINAPI CloseHandle(
|
270
|
-
# _In_ HANDLE hObject
|
271
|
-
# );
|
272
|
-
ffi_lib :kernel32
|
273
|
-
attach_function_private :CloseHandle, [:handle], :win32_bool
|
274
|
-
|
275
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680722(v=vs.85).aspx
|
276
|
-
# void CoTaskMemFree(
|
277
|
-
# _In_opt_ LPVOID pv
|
278
|
-
# );
|
279
|
-
ffi_lib :ole32
|
280
|
-
attach_function :CoTaskMemFree, [:lpvoid], :void
|
281
|
-
end
|
282
|
-
end
|
@@ -1,488 +0,0 @@
|
|
1
|
-
require 'puppet/util/windows'
|
2
|
-
|
3
|
-
module Puppet::Util::Windows::File
|
4
|
-
require 'ffi'
|
5
|
-
extend FFI::Library
|
6
|
-
extend Puppet::Util::Windows::String
|
7
|
-
|
8
|
-
FILE_ATTRIBUTE_READONLY = 0x00000001
|
9
|
-
|
10
|
-
SYNCHRONIZE = 0x100000
|
11
|
-
STANDARD_RIGHTS_REQUIRED = 0xf0000
|
12
|
-
STANDARD_RIGHTS_READ = 0x20000
|
13
|
-
STANDARD_RIGHTS_WRITE = 0x20000
|
14
|
-
STANDARD_RIGHTS_EXECUTE = 0x20000
|
15
|
-
STANDARD_RIGHTS_ALL = 0x1F0000
|
16
|
-
SPECIFIC_RIGHTS_ALL = 0xFFFF
|
17
|
-
|
18
|
-
FILE_READ_DATA = 1
|
19
|
-
FILE_WRITE_DATA = 2
|
20
|
-
FILE_APPEND_DATA = 4
|
21
|
-
FILE_READ_EA = 8
|
22
|
-
FILE_WRITE_EA = 16
|
23
|
-
FILE_EXECUTE = 32
|
24
|
-
FILE_DELETE_CHILD = 64
|
25
|
-
FILE_READ_ATTRIBUTES = 128
|
26
|
-
FILE_WRITE_ATTRIBUTES = 256
|
27
|
-
|
28
|
-
FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
|
29
|
-
|
30
|
-
FILE_GENERIC_READ =
|
31
|
-
STANDARD_RIGHTS_READ |
|
32
|
-
FILE_READ_DATA |
|
33
|
-
FILE_READ_ATTRIBUTES |
|
34
|
-
FILE_READ_EA |
|
35
|
-
SYNCHRONIZE
|
36
|
-
|
37
|
-
FILE_GENERIC_WRITE =
|
38
|
-
STANDARD_RIGHTS_WRITE |
|
39
|
-
FILE_WRITE_DATA |
|
40
|
-
FILE_WRITE_ATTRIBUTES |
|
41
|
-
FILE_WRITE_EA |
|
42
|
-
FILE_APPEND_DATA |
|
43
|
-
SYNCHRONIZE
|
44
|
-
|
45
|
-
FILE_GENERIC_EXECUTE =
|
46
|
-
STANDARD_RIGHTS_EXECUTE |
|
47
|
-
FILE_READ_ATTRIBUTES |
|
48
|
-
FILE_EXECUTE |
|
49
|
-
SYNCHRONIZE
|
50
|
-
|
51
|
-
REPLACEFILE_WRITE_THROUGH = 0x1
|
52
|
-
REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2
|
53
|
-
REPLACEFILE_IGNORE_ACL_ERRORS = 0x3
|
54
|
-
|
55
|
-
def replace_file(target, source)
|
56
|
-
target_encoded = wide_string(target.to_s)
|
57
|
-
source_encoded = wide_string(source.to_s)
|
58
|
-
|
59
|
-
flags = REPLACEFILE_IGNORE_MERGE_ERRORS
|
60
|
-
backup_file = nil
|
61
|
-
result = ReplaceFileW(
|
62
|
-
target_encoded,
|
63
|
-
source_encoded,
|
64
|
-
backup_file,
|
65
|
-
flags,
|
66
|
-
FFI::Pointer::NULL,
|
67
|
-
FFI::Pointer::NULL
|
68
|
-
)
|
69
|
-
|
70
|
-
return true if result != FFI::WIN32_FALSE
|
71
|
-
raise Puppet::Util::Windows::Error.new("ReplaceFile(#{target}, #{source})")
|
72
|
-
end
|
73
|
-
module_function :replace_file
|
74
|
-
|
75
|
-
def move_file_ex(source, target, flags = 0)
|
76
|
-
result = MoveFileExW(wide_string(source.to_s),
|
77
|
-
wide_string(target.to_s),
|
78
|
-
flags)
|
79
|
-
|
80
|
-
return true if result != FFI::WIN32_FALSE
|
81
|
-
raise Puppet::Util::Windows::Error.
|
82
|
-
new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})")
|
83
|
-
end
|
84
|
-
module_function :move_file_ex
|
85
|
-
|
86
|
-
def symlink(target, symlink)
|
87
|
-
flags = File.directory?(target) ? 0x1 : 0x0
|
88
|
-
result = CreateSymbolicLinkW(wide_string(symlink.to_s),
|
89
|
-
wide_string(target.to_s), flags)
|
90
|
-
return true if result != FFI::WIN32_FALSE
|
91
|
-
raise Puppet::Util::Windows::Error.new(
|
92
|
-
"CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})")
|
93
|
-
end
|
94
|
-
module_function :symlink
|
95
|
-
|
96
|
-
|
97
|
-
def exist?(path)
|
98
|
-
path = path.to_str if path.respond_to?(:to_str) # support WatchedFile
|
99
|
-
path = path.to_s # support String and Pathname
|
100
|
-
|
101
|
-
seen_paths = []
|
102
|
-
# follow up to 64 symlinks before giving up
|
103
|
-
0.upto(64) do |depth|
|
104
|
-
# return false if this path has been seen before. This is protection against circular symlinks
|
105
|
-
return false if seen_paths.include?(path.downcase)
|
106
|
-
|
107
|
-
result = get_attributes(path,false)
|
108
|
-
|
109
|
-
# return false for path not found
|
110
|
-
return false if result == INVALID_FILE_ATTRIBUTES
|
111
|
-
|
112
|
-
# return true if path exists and it's not a symlink
|
113
|
-
# Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx
|
114
|
-
return true if (result & FILE_ATTRIBUTE_REPARSE_POINT) != FILE_ATTRIBUTE_REPARSE_POINT
|
115
|
-
|
116
|
-
# walk the symlink and try again...
|
117
|
-
seen_paths << path.downcase
|
118
|
-
path = readlink(path)
|
119
|
-
end
|
120
|
-
|
121
|
-
false
|
122
|
-
end
|
123
|
-
module_function :exist?
|
124
|
-
|
125
|
-
|
126
|
-
INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF #define INVALID_FILE_ATTRIBUTES (DWORD (-1))
|
127
|
-
|
128
|
-
def get_attributes(file_name, raise_on_invalid = true)
|
129
|
-
result = GetFileAttributesW(wide_string(file_name.to_s))
|
130
|
-
if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES
|
131
|
-
raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})")
|
132
|
-
end
|
133
|
-
|
134
|
-
result
|
135
|
-
end
|
136
|
-
module_function :get_attributes
|
137
|
-
|
138
|
-
def add_attributes(path, flags)
|
139
|
-
oldattrs = get_attributes(path)
|
140
|
-
|
141
|
-
if (oldattrs | flags) != oldattrs
|
142
|
-
set_attributes(path, oldattrs | flags)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
module_function :add_attributes
|
146
|
-
|
147
|
-
def remove_attributes(path, flags)
|
148
|
-
oldattrs = get_attributes(path)
|
149
|
-
|
150
|
-
if (oldattrs & ~flags) != oldattrs
|
151
|
-
set_attributes(path, oldattrs & ~flags)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
module_function :remove_attributes
|
155
|
-
|
156
|
-
def set_attributes(path, flags)
|
157
|
-
success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE
|
158
|
-
raise Puppet::Util::Windows::Error.new(_("Failed to set file attributes")) if !success
|
159
|
-
|
160
|
-
success
|
161
|
-
end
|
162
|
-
module_function :set_attributes
|
163
|
-
|
164
|
-
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
|
165
|
-
INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
|
166
|
-
def self.create_file(file_name, desired_access, share_mode, security_attributes,
|
167
|
-
creation_disposition, flags_and_attributes, template_file_handle)
|
168
|
-
|
169
|
-
result = CreateFileW(wide_string(file_name.to_s),
|
170
|
-
desired_access, share_mode, security_attributes, creation_disposition,
|
171
|
-
flags_and_attributes, template_file_handle)
|
172
|
-
|
173
|
-
return result unless result == INVALID_HANDLE_VALUE
|
174
|
-
raise Puppet::Util::Windows::Error.new(
|
175
|
-
"CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " +
|
176
|
-
"#{security_attributes}, #{creation_disposition.to_s(8)}, " +
|
177
|
-
"#{flags_and_attributes.to_s(8)}, #{template_file_handle})")
|
178
|
-
end
|
179
|
-
|
180
|
-
def self.get_reparse_point_data(handle, &block)
|
181
|
-
# must be multiple of 1024, min 10240
|
182
|
-
FFI::MemoryPointer.new(REPARSE_DATA_BUFFER.size) do |reparse_data_buffer_ptr|
|
183
|
-
device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)
|
184
|
-
yield REPARSE_DATA_BUFFER.new(reparse_data_buffer_ptr)
|
185
|
-
end
|
186
|
-
|
187
|
-
# underlying struct MemoryPointer has been cleaned up by this point, nothing to return
|
188
|
-
nil
|
189
|
-
end
|
190
|
-
|
191
|
-
def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
|
192
|
-
if out_buffer.nil?
|
193
|
-
raise Puppet::Util::Windows::Error.new(_("out_buffer is required"))
|
194
|
-
end
|
195
|
-
|
196
|
-
FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|
|
197
|
-
result = DeviceIoControl(
|
198
|
-
handle,
|
199
|
-
io_control_code,
|
200
|
-
in_buffer, in_buffer.nil? ? 0 : in_buffer.size,
|
201
|
-
out_buffer, out_buffer.size,
|
202
|
-
bytes_returned_ptr,
|
203
|
-
nil
|
204
|
-
)
|
205
|
-
|
206
|
-
if result == FFI::WIN32_FALSE
|
207
|
-
raise Puppet::Util::Windows::Error.new(
|
208
|
-
"DeviceIoControl(#{handle}, #{io_control_code}, " +
|
209
|
-
"#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " +
|
210
|
-
"#{out_buffer}, #{out_buffer ? out_buffer.size : ''}")
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
out_buffer
|
215
|
-
end
|
216
|
-
|
217
|
-
FILE_ATTRIBUTE_REPARSE_POINT = 0x400
|
218
|
-
def symlink?(file_name)
|
219
|
-
attributes = get_attributes(file_name, false)
|
220
|
-
|
221
|
-
return false if (attributes == INVALID_FILE_ATTRIBUTES)
|
222
|
-
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
|
223
|
-
end
|
224
|
-
module_function :symlink?
|
225
|
-
|
226
|
-
GENERIC_READ = 0x80000000
|
227
|
-
GENERIC_WRITE = 0x40000000
|
228
|
-
GENERIC_EXECUTE = 0x20000000
|
229
|
-
GENERIC_ALL = 0x10000000
|
230
|
-
FILE_SHARE_READ = 1
|
231
|
-
FILE_SHARE_WRITE = 2
|
232
|
-
OPEN_EXISTING = 3
|
233
|
-
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
|
234
|
-
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
|
235
|
-
|
236
|
-
def self.open_symlink(link_name)
|
237
|
-
begin
|
238
|
-
yield handle = create_file(
|
239
|
-
link_name,
|
240
|
-
GENERIC_READ,
|
241
|
-
FILE_SHARE_READ,
|
242
|
-
nil, # security_attributes
|
243
|
-
OPEN_EXISTING,
|
244
|
-
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
245
|
-
0) # template_file
|
246
|
-
ensure
|
247
|
-
FFI::WIN32.CloseHandle(handle) if handle
|
248
|
-
end
|
249
|
-
|
250
|
-
# handle has had CloseHandle called against it, so nothing to return
|
251
|
-
nil
|
252
|
-
end
|
253
|
-
|
254
|
-
def readlink(link_name)
|
255
|
-
link = nil
|
256
|
-
open_symlink(link_name) do |handle|
|
257
|
-
link = resolve_symlink(handle)
|
258
|
-
end
|
259
|
-
|
260
|
-
link
|
261
|
-
end
|
262
|
-
module_function :readlink
|
263
|
-
|
264
|
-
ERROR_FILE_NOT_FOUND = 2
|
265
|
-
ERROR_PATH_NOT_FOUND = 3
|
266
|
-
|
267
|
-
def get_long_pathname(path)
|
268
|
-
converted = ''
|
269
|
-
FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
|
270
|
-
# includes terminating NULL
|
271
|
-
buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)
|
272
|
-
FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
|
273
|
-
if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
|
274
|
-
raise Puppet::Util::Windows::Error.new(_("Failed to call GetLongPathName"))
|
275
|
-
end
|
276
|
-
|
277
|
-
converted = converted_ptr.read_wide_string(buffer_size - 1)
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
converted
|
282
|
-
end
|
283
|
-
module_function :get_long_pathname
|
284
|
-
|
285
|
-
def get_short_pathname(path)
|
286
|
-
converted = ''
|
287
|
-
FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
|
288
|
-
# includes terminating NULL
|
289
|
-
buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0)
|
290
|
-
FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
|
291
|
-
if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
|
292
|
-
raise Puppet::Util::Windows::Error.new("Failed to call GetShortPathName")
|
293
|
-
end
|
294
|
-
|
295
|
-
converted = converted_ptr.read_wide_string(buffer_size - 1)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
converted
|
300
|
-
end
|
301
|
-
module_function :get_short_pathname
|
302
|
-
|
303
|
-
def stat(file_name)
|
304
|
-
file_name = file_name.to_s # accommodate PathName or String
|
305
|
-
stat = File.stat(file_name)
|
306
|
-
singleton_class = class << stat; self; end
|
307
|
-
target_path = file_name
|
308
|
-
|
309
|
-
if symlink?(file_name)
|
310
|
-
target_path = readlink(file_name)
|
311
|
-
link_ftype = File.stat(target_path).ftype
|
312
|
-
|
313
|
-
# sigh, monkey patch instance method for instance, and close over link_ftype
|
314
|
-
singleton_class.send(:define_method, :ftype) do
|
315
|
-
link_ftype
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
singleton_class.send(:define_method, :mode) do
|
320
|
-
Puppet::Util::Windows::Security.get_mode(target_path)
|
321
|
-
end
|
322
|
-
|
323
|
-
stat
|
324
|
-
end
|
325
|
-
module_function :stat
|
326
|
-
|
327
|
-
def lstat(file_name)
|
328
|
-
file_name = file_name.to_s # accommodate PathName or String
|
329
|
-
# monkey'ing around!
|
330
|
-
stat = File.lstat(file_name)
|
331
|
-
|
332
|
-
singleton_class = class << stat; self; end
|
333
|
-
singleton_class.send(:define_method, :mode) do
|
334
|
-
Puppet::Util::Windows::Security.get_mode(file_name)
|
335
|
-
end
|
336
|
-
|
337
|
-
if symlink?(file_name)
|
338
|
-
def stat.ftype
|
339
|
-
"link"
|
340
|
-
end
|
341
|
-
end
|
342
|
-
stat
|
343
|
-
end
|
344
|
-
module_function :lstat
|
345
|
-
|
346
|
-
private
|
347
|
-
|
348
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
|
349
|
-
FSCTL_GET_REPARSE_POINT = 0x900a8
|
350
|
-
|
351
|
-
def self.resolve_symlink(handle)
|
352
|
-
path = nil
|
353
|
-
get_reparse_point_data(handle) do |reparse_data|
|
354
|
-
offset = reparse_data[:PrintNameOffset]
|
355
|
-
length = reparse_data[:PrintNameLength]
|
356
|
-
|
357
|
-
ptr = reparse_data.pointer + reparse_data.offset_of(:PathBuffer) + offset
|
358
|
-
path = ptr.read_wide_string(length / 2) # length is bytes, need UTF-16 wchars
|
359
|
-
end
|
360
|
-
|
361
|
-
path
|
362
|
-
end
|
363
|
-
|
364
|
-
ffi_convention :stdcall
|
365
|
-
|
366
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx
|
367
|
-
# BOOL WINAPI ReplaceFile(
|
368
|
-
# _In_ LPCTSTR lpReplacedFileName,
|
369
|
-
# _In_ LPCTSTR lpReplacementFileName,
|
370
|
-
# _In_opt_ LPCTSTR lpBackupFileName,
|
371
|
-
# _In_ DWORD dwReplaceFlags - 0x1 REPLACEFILE_WRITE_THROUGH,
|
372
|
-
# 0x2 REPLACEFILE_IGNORE_MERGE_ERRORS,
|
373
|
-
# 0x4 REPLACEFILE_IGNORE_ACL_ERRORS
|
374
|
-
# _Reserved_ LPVOID lpExclude,
|
375
|
-
# _Reserved_ LPVOID lpReserved
|
376
|
-
# );
|
377
|
-
ffi_lib :kernel32
|
378
|
-
attach_function_private :ReplaceFileW,
|
379
|
-
[:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpvoid, :lpvoid], :win32_bool
|
380
|
-
|
381
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
|
382
|
-
# BOOL WINAPI MoveFileEx(
|
383
|
-
# _In_ LPCTSTR lpExistingFileName,
|
384
|
-
# _In_opt_ LPCTSTR lpNewFileName,
|
385
|
-
# _In_ DWORD dwFlags
|
386
|
-
# );
|
387
|
-
ffi_lib :kernel32
|
388
|
-
attach_function_private :MoveFileExW,
|
389
|
-
[:lpcwstr, :lpcwstr, :dword], :win32_bool
|
390
|
-
|
391
|
-
# BOOLEAN WINAPI CreateSymbolicLink(
|
392
|
-
# _In_ LPTSTR lpSymlinkFileName, - symbolic link to be created
|
393
|
-
# _In_ LPTSTR lpTargetFileName, - name of target for symbolic link
|
394
|
-
# _In_ DWORD dwFlags - 0x0 target is a file, 0x1 target is a directory
|
395
|
-
# );
|
396
|
-
# rescue on Windows < 6.0 so that code doesn't explode
|
397
|
-
begin
|
398
|
-
ffi_lib :kernel32
|
399
|
-
attach_function_private :CreateSymbolicLinkW,
|
400
|
-
[:lpwstr, :lpwstr, :dword], :boolean
|
401
|
-
rescue LoadError
|
402
|
-
end
|
403
|
-
|
404
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx
|
405
|
-
# DWORD WINAPI GetFileAttributes(
|
406
|
-
# _In_ LPCTSTR lpFileName
|
407
|
-
# );
|
408
|
-
ffi_lib :kernel32
|
409
|
-
attach_function_private :GetFileAttributesW,
|
410
|
-
[:lpcwstr], :dword
|
411
|
-
|
412
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx
|
413
|
-
# BOOL WINAPI SetFileAttributes(
|
414
|
-
# _In_ LPCTSTR lpFileName,
|
415
|
-
# _In_ DWORD dwFileAttributes
|
416
|
-
# );
|
417
|
-
ffi_lib :kernel32
|
418
|
-
attach_function_private :SetFileAttributesW,
|
419
|
-
[:lpcwstr, :dword], :win32_bool
|
420
|
-
|
421
|
-
# HANDLE WINAPI CreateFile(
|
422
|
-
# _In_ LPCTSTR lpFileName,
|
423
|
-
# _In_ DWORD dwDesiredAccess,
|
424
|
-
# _In_ DWORD dwShareMode,
|
425
|
-
# _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
426
|
-
# _In_ DWORD dwCreationDisposition,
|
427
|
-
# _In_ DWORD dwFlagsAndAttributes,
|
428
|
-
# _In_opt_ HANDLE hTemplateFile
|
429
|
-
# );
|
430
|
-
ffi_lib :kernel32
|
431
|
-
attach_function_private :CreateFileW,
|
432
|
-
[:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle
|
433
|
-
|
434
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
|
435
|
-
# BOOL WINAPI DeviceIoControl(
|
436
|
-
# _In_ HANDLE hDevice,
|
437
|
-
# _In_ DWORD dwIoControlCode,
|
438
|
-
# _In_opt_ LPVOID lpInBuffer,
|
439
|
-
# _In_ DWORD nInBufferSize,
|
440
|
-
# _Out_opt_ LPVOID lpOutBuffer,
|
441
|
-
# _In_ DWORD nOutBufferSize,
|
442
|
-
# _Out_opt_ LPDWORD lpBytesReturned,
|
443
|
-
# _Inout_opt_ LPOVERLAPPED lpOverlapped
|
444
|
-
# );
|
445
|
-
ffi_lib :kernel32
|
446
|
-
attach_function_private :DeviceIoControl,
|
447
|
-
[:handle, :dword, :lpvoid, :dword, :lpvoid, :dword, :lpdword, :pointer], :win32_bool
|
448
|
-
|
449
|
-
MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384
|
450
|
-
|
451
|
-
# REPARSE_DATA_BUFFER
|
452
|
-
# https://msdn.microsoft.com/en-us/library/cc232006.aspx
|
453
|
-
# https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
|
454
|
-
# struct is always MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes
|
455
|
-
class REPARSE_DATA_BUFFER < FFI::Struct
|
456
|
-
layout :ReparseTag, :win32_ulong,
|
457
|
-
:ReparseDataLength, :ushort,
|
458
|
-
:Reserved, :ushort,
|
459
|
-
:SubstituteNameOffset, :ushort,
|
460
|
-
:SubstituteNameLength, :ushort,
|
461
|
-
:PrintNameOffset, :ushort,
|
462
|
-
:PrintNameLength, :ushort,
|
463
|
-
:Flags, :win32_ulong,
|
464
|
-
# max less above fields dword / uint 4 bytes, ushort 2 bytes
|
465
|
-
# technically a WCHAR buffer, but we care about size in bytes here
|
466
|
-
:PathBuffer, [:byte, MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 20]
|
467
|
-
end
|
468
|
-
|
469
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
|
470
|
-
# DWORD WINAPI GetLongPathName(
|
471
|
-
# _In_ LPCTSTR lpszShortPath,
|
472
|
-
# _Out_ LPTSTR lpszLongPath,
|
473
|
-
# _In_ DWORD cchBuffer
|
474
|
-
# );
|
475
|
-
ffi_lib :kernel32
|
476
|
-
attach_function_private :GetLongPathNameW,
|
477
|
-
[:lpcwstr, :lpwstr, :dword], :dword
|
478
|
-
|
479
|
-
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
|
480
|
-
# DWORD WINAPI GetShortPathName(
|
481
|
-
# _In_ LPCTSTR lpszLongPath,
|
482
|
-
# _Out_ LPTSTR lpszShortPath,
|
483
|
-
# _In_ DWORD cchBuffer
|
484
|
-
# );
|
485
|
-
ffi_lib :kernel32
|
486
|
-
attach_function_private :GetShortPathNameW,
|
487
|
-
[:lpcwstr, :lpwstr, :dword], :dword
|
488
|
-
end
|