File Coverage

blib/lib/Parse/Win32Registry/Win95/Key.pm
Criterion Covered Total %
statement 104 110 94.5
branch 26 32 81.2
condition n/a
subroutine 19 21 90.4
pod 0 11 0.0
total 149 174 85.6


line stmt bran cond sub pod time code
1             package Parse::Win32Registry::Win95::Key;
2              
3 13     13   71 use strict;
  13         23  
  13         446  
4 13     13   66 use warnings;
  13         25  
  13         360  
5              
6 13     13   62 use base qw(Parse::Win32Registry::Key);
  13         26  
  13         7787  
7              
8 13     13   88 use Carp;
  13         26  
  13         735  
9 13     13   70 use Parse::Win32Registry::Base qw(:all);
  13         21  
  13         4599  
10 13     13   10055 use Parse::Win32Registry::Win95::Value;
  13         41  
  13         470  
11              
12 13     13   83 use constant RGKN_ENTRY_LENGTH => 0x1c;
  13         29  
  13         760  
13 13     13   75 use constant OFFSET_TO_RGKN_BLOCK => 0x20;
  13         30  
  13         18556  
14              
15             sub new {
16 332     332 0 663 my $class = shift;
17 332         399 my $regfile = shift;
18 332         381 my $offset = shift; # offset to RGKN key entry relative to start of RGKN
19 332         395 my $parent_key_path = shift; # parent key path (optional)
20              
21 332 50       787 croak 'Missing registry file' if !defined $regfile;
22 332 50       662 croak 'Missing offset' if !defined $offset;
23              
24 332         1041 my $fh = $regfile->get_filehandle;
25              
26             # RGKN Key Entry
27             # 0x00 dword
28             # 0x04 dword
29             # 0x08 dword
30             # 0x0c dword = offset to parent RGKN entry
31             # 0x10 dword = offset to first child RGKN entry
32             # 0x14 dword = offset to next sibling RGKN entry
33             # 0x18 dword = entry id of RGDB entry
34              
35             # Extracted offsets are relative to the start of the RGKN block
36              
37             # Any offset of 0xffffffff marks the end of a list.
38             # An entry id of 0xffffffff means the RGKN entry has no RGDB entry.
39             # This occurs for the root key of the registry file.
40              
41 332         1717 sysseek($fh, $offset, 0);
42 332         2068 my $bytes_read = sysread($fh, my $rgkn_entry, RGKN_ENTRY_LENGTH);
43 332 100       791 if ($bytes_read != RGKN_ENTRY_LENGTH) {
44 1         7 warnf('Could not read RGKN key at 0x%x', $offset);
45 1         17 return;
46             }
47              
48 331         1061 my ($offset_to_parent,
49             $offset_to_first_child,
50             $offset_to_next_sibling,
51             $key_id) = unpack('x12VVVV', $rgkn_entry);
52              
53 331 100       1176 $offset_to_parent += OFFSET_TO_RGKN_BLOCK
54             if $offset_to_parent != 0xffffffff;
55 331 100       684 $offset_to_first_child += OFFSET_TO_RGKN_BLOCK
56             if $offset_to_first_child != 0xffffffff;
57 331 100       875 $offset_to_next_sibling += OFFSET_TO_RGKN_BLOCK
58             if $offset_to_next_sibling != 0xffffffff;
59              
60 331         672 my $self = {};
61 331         769 $self->{_regfile} = $regfile;
62 331         464 $self->{_offset} = $offset;
63 331         499 $self->{_length} = RGKN_ENTRY_LENGTH;
64 331         465 $self->{_allocated} = 1;
65 331         1102 $self->{_tag} = 'rgkn key';
66 331         489 $self->{_offset_to_parent} = $offset_to_parent;
67 331         655 $self->{_offset_to_first_child} = $offset_to_first_child;
68 331         943 $self->{_offset_to_next_sibling} = $offset_to_next_sibling;
69 331         582 $self->{_id} = $key_id;
70 331         861 bless $self, $class;
71              
72             # Look up corresponding rgdb entry
73 331         487 my $index = $regfile->{_rgdb_index};
74 331 50       748 croak 'Missing rgdb index' if !defined $index;
75 331 100       837 if (exists $index->{$key_id}) {
76 299         546 my $rgdb_key = $index->{$key_id};
77 299         579 $self->{_rgdb_key} = $rgdb_key;
78 299         937 $self->{_name} = $rgdb_key->get_name;
79             }
80             else {
81 32         141 $self->{_name} = '';
82             # Only the root key should have no matching RGDB entry
83 32 100       96 if (!$self->is_root) {
84 1         5 warnf('Could not find RGDB entry for RGKN key at 0x%x', $offset);
85             }
86             }
87              
88 331         597 my $name = $self->{_name};
89 331 100       1077 $self->{_key_path} = defined($parent_key_path)
90             ? "$parent_key_path\\$name"
91             : $name;
92              
93 331         7100 return $self;
94             }
95              
96             sub get_timestamp {
97 33     33 0 14909 return undef;
98             }
99              
100             sub get_timestamp_as_string {
101 34     34 0 7485 return iso8601(undef);
102             }
103              
104             sub get_class_name {
105 17     17 0 89 return undef;
106             }
107              
108             sub is_root {
109 146     146 0 9411 my $self = shift;
110              
111 146         312 my $offset = $self->{_offset};
112 146         214 my $regfile = $self->{_regfile};
113              
114 146         425 my $rgkn_block = $regfile->get_rgkn;
115 146         268 my $offset_to_root_key = $rgkn_block->{_offset_to_root_key};
116              
117             # This gives better results than checking id == 0xffffffff
118 146         565 return $offset == $offset_to_root_key;
119             }
120              
121             sub get_parent {
122 48     48 0 83 my $self = shift;
123              
124 48         84 my $regfile = $self->{_regfile};
125 48         86 my $offset_to_parent = $self->{_offset_to_parent};
126 48         76 my $key_path = $self->{_key_path};
127              
128 48 50       118 return if $self->is_root;
129              
130 48         68 my $grandparent_key_path;
131 48         231 my @keys = split(/\\/, $key_path, -1);
132 48 100       137 if (@keys > 2) {
133 28         145 $grandparent_key_path = join("\\", @keys[0..$#keys-2]);
134             }
135              
136 48         187 return Parse::Win32Registry::Win95::Key->new($regfile,
137             $offset_to_parent,
138             $grandparent_key_path);
139             }
140              
141             sub get_security {
142 0     0 0 0 return undef;
143             }
144              
145             sub as_string {
146 23     23 0 7335 my $self = shift;
147              
148 23         107 return $self->get_path;
149             }
150              
151             sub parse_info {
152 0     0 0 0 my $self = shift;
153              
154 0         0 my $info = sprintf '0x%x rgkn key len=0x%x par=0x%x,child=0x%x,next=0x%x id=0x%x',
155             $self->{_offset},
156             $self->{_length},
157             $self->{_offset_to_parent},
158             $self->{_offset_to_first_child},
159             $self->{_offset_to_next_sibling},
160             $self->{_id};
161 0         0 return $info;
162             }
163              
164             sub get_subkey_iterator {
165 126     126 0 195 my $self = shift;
166              
167 126         224 my $regfile = $self->{_regfile};
168 126         232 my $key_path = $self->{_key_path};
169              
170 126         226 my $offset_to_next_key = $self->{_offset_to_first_child};
171              
172 126         488 my $end_of_file = $regfile->get_length;
173 126         426 my $rgkn_block = $regfile->get_rgkn;
174 126         492 my $end_of_rgkn_block = $rgkn_block->get_offset + $rgkn_block->get_length;
175              
176             return Parse::Win32Registry::Iterator->new(sub {
177 397 100   397   984 if ($offset_to_next_key == 0xffffffff) {
178 126         408 return; # no more subkeys
179             }
180 271 50       572 if ($offset_to_next_key > $end_of_rgkn_block) {
181 0         0 return;
182             }
183 271 50       750 if (my $key = Parse::Win32Registry::Win95::Key->new($regfile,
184             $offset_to_next_key, $key_path))
185             {
186 271         382 $offset_to_next_key = $key->{_offset_to_next_sibling};
187 271         977 return $key;
188             }
189             else {
190 0         0 return; # no more subkeys
191             }
192 126         943 });
193             }
194              
195             sub get_value_iterator {
196 151     151 0 198 my $self = shift;
197              
198 151         293 my $rgdb_key = $self->{_rgdb_key};
199 151 100       339 if (defined $rgdb_key) {
200 140         682 return $rgdb_key->get_value_iterator;
201             }
202             else {
203 11     8   109 return Parse::Win32Registry::Iterator->new(sub {});
  8         30  
204             }
205             }
206              
207             1;