virt_disk 0.0.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.
@@ -0,0 +1,71 @@
1
+ require 'uuidtools'
2
+
3
+ module VirtDisk
4
+ module PartitionType
5
+ class GptPartition < Partition
6
+ GPT_HEADER = BinaryStruct.new([
7
+ 'a8', :signature, # 00-07: Signature "EFI PART"
8
+ 'a4', :version, # 08-11: Revision
9
+ 'L', :header_size, # 12-15: Header size
10
+ 'L', :crc32_header, # 16-19:
11
+ 'L', :reserved, # 20-23:
12
+ 'Q', :cur_lba, # 24-31:
13
+ 'Q', :bak_lba, # 32-39:
14
+ 'Q', :first_lba, # 40-47:
15
+ 'Q', :last_lba, # 48-55:
16
+ 'a16', :guid, # 56-71:
17
+ 'Q', :startLBA, # 72-79:
18
+ 'L', :part_num, # 80-83:
19
+ 'L', :part_size, # 84-87:
20
+ 'L', :part_array, # 88-91:
21
+ 'a420', :reserved2, # 92-511:
22
+ ])
23
+
24
+ GPT_PARTITION_ENTRY = BinaryStruct.new([
25
+ 'a16', :ptype, # 00-15: partition type
26
+ 'a16', :pguid, # 16-31: partition GUID
27
+ 'Q', :first_lba, # 32-39: first LBA
28
+ 'Q', :last_lba, # 40-47: last LBA
29
+ 'a8', :attr_flag, # 48-55: attribute flag
30
+ 'a72', :pname, # 56-127: partition name
31
+ ])
32
+
33
+ include LogDecorator::Logging
34
+ include ExportMethods
35
+
36
+ def self.discover_partitions(disk) # rubocop:disable AbcSize
37
+ mbr = disk.mod_read(0, MBR_SIZE)
38
+ if mbr.length < MBR_SIZE
39
+ _log.info "<#{disk.object_id}> disk does not contain a master boot record"
40
+ return []
41
+ end
42
+
43
+ sig = mbr[510..511].unpack('H4')
44
+
45
+ pt_entry = DOS_PARTITION_ENTRY.decode(mbr[DOS_PT_START, PTE_LEN])
46
+ ptype = pt_entry[:ptype]
47
+
48
+ return [] if sig[0] != DOS_SIG || ptype != GPT_SIG
49
+ discover_gpt_partitions(disk)
50
+ end
51
+
52
+ def self.discover_gpt_partitions(disk) # rubocop:disable AbcSize
53
+ _log.info "Parsing GPT disk ..."
54
+ gpt_header = disk.mod_read(MBR_SIZE, GPT_HEADER.size)
55
+ header = GPT_HEADER.decode(gpt_header)
56
+
57
+ partitions = []
58
+ pte = GPT_HEADER.size + MBR_SIZE
59
+ (1..header[:part_num]).each do |n|
60
+ gpt = disk.mod_read(pte, GPT_PARTITION_ENTRY.size)
61
+ pt_entry = GPT_PARTITION_ENTRY.decode(gpt)
62
+ ptype = UUIDTools::UUID.parse_raw(pt_entry[:ptype]).to_s
63
+
64
+ partitions.push(new(disk, ptype, n, pt_entry[:first_lba], pt_entry[:last_lba])) if pt_entry[:first_lba] != 0
65
+ pte += header[:part_size]
66
+ end
67
+ partitions
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module VirtDisk
2
+ VERSION = "0.0.1" unless const_defined?(:VERSION)
3
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+ require "partition_shared_examples"
3
+
4
+ describe "DOS Partition" do
5
+ let(:extpected_num_partitions) { 2 }
6
+ let(:expected_partition_class) { VirtDisk::PartitionType::DosPartition }
7
+
8
+ let(:per_partition_values) do
9
+ [
10
+ {
11
+ :ptype => 4,
12
+ :block_size => 512,
13
+ :start_lba => 63,
14
+ :end_lba => 575,
15
+ :start_byte_addr => 32256,
16
+ :end_byte_addr => 294400,
17
+ :size => 262144
18
+ },
19
+ {
20
+ :ptype => 4,
21
+ :block_size => 512,
22
+ :start_lba => 575,
23
+ :end_lba => 2048,
24
+ :start_byte_addr => 294400,
25
+ :end_byte_addr => 1048576,
26
+ :size => 754176
27
+ }
28
+ ]
29
+ end
30
+
31
+ data_dir = File.join(__dir__, "data")
32
+ dos_partition_file = File.join(data_dir, "dos_partition.img")
33
+ file_mod = VirtDisk::FileIo.new(dos_partition_file)
34
+ disk = VirtDisk::Disk.new(file_mod)
35
+
36
+ it "should return an array of the expected length" do
37
+ expect(VirtDisk::PartitionType.partition_probe(disk).length).to eq(extpected_num_partitions)
38
+ end
39
+
40
+ VirtDisk::PartitionType.partition_probe(disk).each do |part|
41
+ describe "Partition: #{part.pnum}" do
42
+ before(:each) do
43
+ @partition = part
44
+ end
45
+
46
+ it_should_behave_like "common_partition"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,257 @@
1
+ require "spec_helper"
2
+ require "virt_disk"
3
+
4
+ def new_test_class(cname, methods_exported = [], methods_not_exported = [])
5
+ klass = Class.new { include VirtDisk::ExportMethods }
6
+ stub_const cname, klass
7
+ add_test_methods(klass, methods_exported, methods_not_exported)
8
+ klass
9
+ end
10
+
11
+ def add_test_methods(klass, methods_exported, methods_not_exported)
12
+ klass.class_eval do
13
+ (methods_exported + methods_not_exported).each do |mn|
14
+ define_method(mn) { "In #{self.class.name}##{mn}" }
15
+ end
16
+ export(*methods_exported)
17
+ end
18
+ end
19
+
20
+ describe VirtDisk::ExportMethods do
21
+ let(:group_a_methods) do
22
+ %i( method1 method2 method3 )
23
+ end
24
+
25
+ let(:group_b_methods) do
26
+ %i( method4 method5 method6 )
27
+ end
28
+
29
+ let(:group_c_methods) do
30
+ %i( method7 )
31
+ end
32
+
33
+ describe "defined methods" do
34
+ before(:each) do
35
+ new_test_class('TestMod1')
36
+ end
37
+
38
+ describe "Class methods" do
39
+ it "should respond to export" do
40
+ expect(TestMod1.respond_to?(:export)).to be true
41
+ end
42
+
43
+ it "should respond to exports" do
44
+ expect(TestMod1.respond_to?(:exports)).to be true
45
+ end
46
+
47
+ it "should respond to exported?" do
48
+ expect(TestMod1.respond_to?(:exported?)).to be true
49
+ end
50
+ end
51
+
52
+ describe "Instance methods" do
53
+ let(:mod1_obj) { TestMod1.new }
54
+
55
+ it "should respond to exported?" do
56
+ expect(mod1_obj.respond_to?(:exported?)).to be true
57
+ end
58
+
59
+ it "should respond to delegate=" do
60
+ expect(mod1_obj.respond_to?(:delegate=)).to be true
61
+ end
62
+
63
+ it "should respond to delegate" do
64
+ expect(mod1_obj.respond_to?(:delegate)).to be true
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "Class methods" do
70
+ describe "export" do
71
+ before(:each) do
72
+ new_test_class('TestMod1', group_a_methods, group_c_methods)
73
+ end
74
+
75
+ it "should raise an error when method isn't defined" do
76
+ expect do
77
+ TestMod1.export(:foo)
78
+ end.to raise_exception(RuntimeError, "Method not defined in class: foo")
79
+ end
80
+
81
+ it "should return nil on success" do
82
+ expect(TestMod1.export(group_c_methods.first)).to be_nil
83
+ end
84
+
85
+ it "should add the method to the exports list" do
86
+ meth = group_c_methods.first
87
+ expect(TestMod1.exports.include?(meth)).to be false
88
+ TestMod1.export(meth)
89
+ expect(TestMod1.exports.include?(meth)).to be true
90
+ end
91
+ end
92
+
93
+ describe "exports" do
94
+ before(:each) do
95
+ new_test_class('TestMod1', [], group_a_methods)
96
+ end
97
+
98
+ it "should return an empty array when nothing exported" do
99
+ expect(TestMod1.exports).to match_array([])
100
+ end
101
+
102
+ it "should return the expected array" do
103
+ expect(TestMod1.exports).to match_array([])
104
+ TestMod1.export(*group_a_methods)
105
+ expect(TestMod1.exports).to match_array(group_a_methods)
106
+ end
107
+ end
108
+
109
+ describe "exported?" do
110
+ before(:each) do
111
+ new_test_class('TestMod1', [], group_a_methods)
112
+ end
113
+
114
+ it "should return false when nothing exported" do
115
+ expect(TestMod1.exported?(group_a_methods.first)).to be false
116
+ end
117
+
118
+ it "should return false when method isn't exported" do
119
+ TestMod1.export(*group_a_methods)
120
+ expect(TestMod1.exported?(group_b_methods.first)).to be false
121
+ end
122
+
123
+ it "should return true when method is exported" do
124
+ TestMod1.export(*group_a_methods)
125
+ expect(TestMod1.exported?(group_a_methods.first)).to be true
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "Instance methods" do
131
+ let(:no_export_obj) do
132
+ new_test_class('NoExportMod')
133
+ NoExportMod.new
134
+ end
135
+
136
+ let(:export_obj) do
137
+ new_test_class('ExportMod', group_a_methods)
138
+ ExportMod.new
139
+ end
140
+
141
+ describe "exported?" do
142
+ it "should return false when nothing exported" do
143
+ expect(no_export_obj.exported?(group_a_methods.first)).to be false
144
+ end
145
+
146
+ it "should return false when method isn't exported" do
147
+ expect(export_obj.exported?(group_b_methods.first)).to be false
148
+ end
149
+
150
+ it "should return true when method is exported" do
151
+ expect(export_obj.exported?(group_a_methods.first)).to be true
152
+ end
153
+ end
154
+
155
+ describe "delegate, delegate=" do
156
+ it "should return nil when no delegate" do
157
+ expect(no_export_obj.delegate).to be nil
158
+ end
159
+
160
+ it "should return the object it is passed" do
161
+ expect(no_export_obj.delegate = export_obj).to eq(export_obj)
162
+ end
163
+
164
+ it "should set the delegate accordingly" do
165
+ no_export_obj.delegate = export_obj
166
+ expect(no_export_obj.delegate).to eq(export_obj)
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "Operation" do
172
+ let(:obja) do
173
+ new_test_class('ModA', group_a_methods)
174
+ ModA.new
175
+ end
176
+
177
+ let(:objb) do
178
+ new_test_class('ModB', group_b_methods)
179
+ ModB.new
180
+ end
181
+
182
+ let(:objc) do
183
+ new_test_class('ModC', group_c_methods)
184
+ ModC.new
185
+ end
186
+
187
+ it "should 'respond_to?' top-level methods" do
188
+ expect(obja.respond_to?(group_a_methods.first)).to eq true
189
+ end
190
+
191
+ it "should call top-level methods directly" do
192
+ method_name = group_a_methods.first
193
+ expect(obja.send(method_name)).to eq("In #{obja.class.name}##{method_name}")
194
+ end
195
+
196
+ it "should 'respond_to?' 2nd-level methods" do
197
+ expect(obja.respond_to?(group_a_methods.first)).to eq true
198
+ expect(obja.respond_to?(group_b_methods.first)).to eq false
199
+
200
+ obja.delegate = objb
201
+ expect(obja.respond_to?(group_b_methods.first)).to eq true
202
+ end
203
+
204
+ it "should call 2nd-level methods" do
205
+ method_a_name = group_a_methods.first
206
+ method_b_name = group_b_methods.first
207
+
208
+ expect(obja.send(method_a_name)).to eq("In #{obja.class.name}##{method_a_name}")
209
+
210
+ expect do
211
+ obja.send(method_b_name)
212
+ end.to raise_exception(NoMethodError, /undefined method `#{method_b_name}' for.*/)
213
+
214
+ obja.delegate = objb
215
+ expect(obja.send(method_b_name)).to eq("In #{objb.class.name}##{method_b_name}")
216
+ end
217
+
218
+ it "should 'respond_to?' 3rd-level methods" do
219
+ expect(obja.respond_to?(group_a_methods.first)).to eq true
220
+ expect(obja.respond_to?(group_b_methods.first)).to eq false
221
+ expect(obja.respond_to?(group_c_methods.first)).to eq false
222
+
223
+ obja.delegate = objb
224
+ expect(obja.respond_to?(group_b_methods.first)).to eq true
225
+ expect(obja.respond_to?(group_c_methods.first)).to eq false
226
+
227
+ objb.delegate = objc
228
+ expect(obja.respond_to?(group_c_methods.first)).to eq true
229
+ end
230
+
231
+ it "should call 3rd-level methods" do
232
+ method_a_name = group_a_methods.first
233
+ method_b_name = group_b_methods.first
234
+ method_c_name = group_c_methods.first
235
+
236
+ expect(obja.send(method_a_name)).to eq("In #{obja.class.name}##{method_a_name}")
237
+
238
+ expect do
239
+ obja.send(method_b_name)
240
+ end.to raise_exception(NoMethodError, /undefined method `#{method_b_name}' for.*/)
241
+
242
+ expect do
243
+ obja.send(method_c_name)
244
+ end.to raise_exception(NoMethodError, /undefined method `#{method_c_name}' for.*/)
245
+
246
+ obja.delegate = objb
247
+ expect(obja.send(method_b_name)).to eq("In #{objb.class.name}##{method_b_name}")
248
+
249
+ expect do
250
+ obja.send(method_c_name)
251
+ end.to raise_exception(NoMethodError, /undefined method `#{method_c_name}' for.*/)
252
+
253
+ objb.delegate = objc
254
+ expect(obja.send(method_c_name)).to eq("In #{objc.class.name}##{method_c_name}")
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+ require "partition_shared_examples"
3
+
4
+ describe "GPT Partition" do
5
+ let(:extpected_num_partitions) { 3 }
6
+ let(:expected_partition_class) { VirtDisk::PartitionType::GptPartition }
7
+
8
+ let(:per_partition_values) do
9
+ [
10
+ {
11
+ :ptype => "af3dc60f-8384-7247-8e79-3d69d8477de4",
12
+ :block_size => 512,
13
+ :start_lba => 34,
14
+ :end_lba => 512,
15
+ :start_byte_addr => 17408,
16
+ :end_byte_addr => 262144,
17
+ :size => 244736
18
+ },
19
+ {
20
+ :ptype => "af3dc60f-8384-7247-8e79-3d69d8477de4",
21
+ :block_size => 512,
22
+ :start_lba => 513,
23
+ :end_lba => 1024,
24
+ :start_byte_addr => 262656,
25
+ :end_byte_addr => 524288,
26
+ :size => 261632
27
+ },
28
+ {
29
+ :ptype => "af3dc60f-8384-7247-8e79-3d69d8477de4",
30
+ :block_size => 512,
31
+ :start_lba => 1025,
32
+ :end_lba => 2014,
33
+ :start_byte_addr => 524800,
34
+ :end_byte_addr => 1031168,
35
+ :size => 506368
36
+ }
37
+ ]
38
+ end
39
+
40
+ data_dir = File.join(__dir__, "data")
41
+ dos_partition_file = File.join(data_dir, "gpt_partition.img")
42
+ file_mod = VirtDisk::FileIo.new(dos_partition_file)
43
+ disk = VirtDisk::Disk.new(file_mod)
44
+
45
+ it "should return an array of the expected length" do
46
+ expect(VirtDisk::PartitionType.partition_probe(disk).length).to eq(extpected_num_partitions)
47
+ end
48
+
49
+ VirtDisk::PartitionType.partition_probe(disk).each do |part|
50
+ describe "Partition: #{part.pnum}" do
51
+ before(:each) do
52
+ @partition = part
53
+ end
54
+
55
+ it_should_behave_like "common_partition"
56
+ end
57
+ end
58
+ end