pdk 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|