Showing with 64 additions and 27 deletions.
  1. +14 −1 CHANGELOG.md
  2. +4 −24 lib/puppet/provider/registry_value/registry.rb
  3. +1 −1 metadata.json
  4. +45 −1 spec/unit/puppet/provider/registry_value_spec.rb
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
##2015-07-38 - Supported Release 1.1.1
##2015-08-13 - SUpported Release 1.1.2
###Summary

Fix critical bug when writing dword and qword values

####Bugfixes
- Fix the way we write dword and qword values [MODULES-2409](https://tickets.puppetlabs.com/browse/MODULES-2409)
- changed byte conversion to use pack instead
- Added tests to catch scenario

##~~2015-08-12 - Supported Release 1.1.1~~ - Deleted
###Summary

This release adds Puppet Enterprise 2015.2.0 to metadata

####Features
- Testcase fixes
- Gemfile updates
- Updated the logic used to convert to byte arrays
- [MODULES-1921](https://tickets.puppetlabs.com/browse/MODULES-1921) Fixes for:
-- Ruby registry writes corrupt string [PR # 93](https://github.com/puppetlabs/puppetlabs-registry/commit/0b99718bc7f2d48752aa976d1ba30e49803e97f1)

##2015-03-24 - Supported Release 1.1.0
###Summary
Expand Down
28 changes: 4 additions & 24 deletions lib/puppet/provider/registry_value/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,28 +174,6 @@ def write_value
end
end

BITS_PER_BYTE = 8
BYTE_MASK = 0xFF

def to_byte_array(num, of_length)
bytes = []
while
# mask only least significant byte and prepend
bytes.unshift(num & BYTE_MASK)

# nothing left to shave off
break if (num <= BYTE_MASK)

# shift off the least significant byte and continue
num >>= BITS_PER_BYTE
end

pad = of_length - bytes.length
bytes.concat([0] * pad) if pad > 0

bytes
end

def data_to_bytes(type, data)
bytes = []

Expand All @@ -210,9 +188,11 @@ def data_to_bytes(type, data)
when Win32::Registry::REG_BINARY
bytes = data.bytes.to_a
when Win32::Registry::REG_DWORD
bytes = to_byte_array(data, FFI::Type::UINT32.size)
# L is 32-bit unsigned native (little) endian order
bytes = [data].pack('L').unpack('C*')
when Win32::Registry::REG_QWORD
bytes = to_byte_array(data, FFI::Type::UINT64.size)
# Q is 64-bit unsigned native (little) endian order
bytes = [data].pack('Q').unpack('C*')
else
raise TypeError, "Unsupported type #{type}"
end
Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-registry",
"version": "1.1.1",
"version": "1.1.2",
"author": "puppetlabs",
"summary": "This module provides a native type and provider to manage keys and values in the Windows Registry",
"license": "Apache-2.0",
Expand Down
46 changes: 45 additions & 1 deletion spec/unit/puppet/provider/registry_value_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
# also, expect that we're not using Rubys each_key / each_value which exhibit bad behavior
Win32::Registry.any_instance.expects(:each_key).never
Win32::Registry.any_instance.expects(:each_value).never

# this covers []= write_s write_i and write_bin
Win32::Registry.any_instance.expects(:write).never
end
end

Expand Down Expand Up @@ -93,6 +96,7 @@ def create_and_destroy(path, reg_type, data)

reg_value.provider.create
reg_value.provider.exists?.should be_true
expect(reg_value.provider.data).to eq([data].flatten)

reg_value.provider.destroy
reg_value.provider.exists?.should be_false
Expand All @@ -107,7 +111,7 @@ def create_and_destroy(path, reg_type, data)
end

it "can destroy a randomly created REG_BINARY value" do
create_and_destroy(path, :binary, '01011010')
create_and_destroy(path, :binary, '01 01 10 10')
end

it "can destroy a randomly created REG_DWORD value" do
Expand All @@ -123,6 +127,46 @@ def create_and_destroy(path, reg_type, data)
end
end

context "when writing numeric values" do
let (:path) { path = "hklm\\#{puppet_key}\\#{subkey_name}\\#{SecureRandom.uuid}" }

after(:each) do
reg_value = type.new(:path => path, :provider => described_class.name)

reg_value.provider.destroy
expect(reg_value.provider).to_not be_exists
end

def write_and_read_value(path, reg_type, value)
reg_value = type.new(:path => path,
:type => reg_type,
:data => value,
:provider => described_class.name)

reg_value.provider.create
expect(reg_value.provider).to be_exists
expect(reg_value.provider.type).to eq(reg_type)

written = reg_value.provider.data.first
expect(written).to eq(value)
end


# values chosen at 1 bit past previous byte boundary
[0xFF + 1, 0xFFFF + 1, 0xFFFFFF + 1, 0xFFFFFFFF].each do |value|
it "properly round-trips written values by converting endianness properly" do
write_and_read_value(path, :dword, value)
write_and_read_value(path, :qword, value)
end
end

[0xFFFFFFFFFF + 1, 0xFFFFFFFFFFFF + 1, 0xFFFFFFFFFFFFFF + 1, 0xFFFFFFFFFFFFFFFF].each do |value|
it "properly round-trips written values by converting endianness properly" do
write_and_read_value(path, :qword, value)
end
end
end

context "when reading non-ASCII values" do
ENDASH_UTF_8 = [0xE2, 0x80, 0x93]
ENDASH_UTF_16 = [0x2013]
Expand Down