virt_disk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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