resilience 0.0.2 → 0.1.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/bin/axe.rb +18 -0
  3. data/bin/fcomp.rb +15 -0
  4. data/bin/pex.rb +16 -0
  5. data/bin/rarser.rb +15 -0
  6. data/bin/reach.rb +16 -0
  7. data/bin/rex.rb +7 -66
  8. data/bin/rinfo.rb +19 -0
  9. data/lib/resilience.rb +5 -0
  10. data/lib/resilience/attribute.rb +21 -4
  11. data/lib/resilience/cli/all.rb +14 -0
  12. data/lib/resilience/cli/bin/axe.rb +34 -0
  13. data/lib/resilience/cli/bin/fcomp.rb +28 -0
  14. data/lib/resilience/cli/bin/pex.rb +43 -0
  15. data/lib/resilience/cli/bin/rarser.rb +72 -0
  16. data/lib/resilience/cli/bin/reach.rb +34 -0
  17. data/lib/resilience/cli/bin/rex.rb +33 -0
  18. data/lib/resilience/cli/bin/rinfo.rb +19 -0
  19. data/lib/resilience/cli/conf.rb +14 -0
  20. data/lib/resilience/cli/default.rb +17 -0
  21. data/lib/resilience/cli/disk.rb +40 -0
  22. data/lib/resilience/cli/file.rb +23 -0
  23. data/lib/resilience/cli/image.rb +45 -0
  24. data/lib/resilience/cli/metadata.rb +24 -0
  25. data/lib/resilience/cli/output.rb +84 -0
  26. data/lib/resilience/collections/dirs.rb +8 -0
  27. data/lib/resilience/collections/files.rb +41 -0
  28. data/lib/resilience/collections/pages.rb +16 -0
  29. data/lib/resilience/conf.rb +28 -0
  30. data/lib/resilience/constants.rb +31 -7
  31. data/lib/resilience/core_ext.rb +39 -0
  32. data/lib/resilience/fs_dir.rb +3 -0
  33. data/lib/resilience/fs_dir/dir_base.rb +16 -6
  34. data/lib/resilience/fs_dir/dir_entry.rb +33 -0
  35. data/lib/resilience/fs_dir/file_entry.rb +53 -0
  36. data/lib/resilience/image.rb +33 -0
  37. data/lib/resilience/mixins/on_image.rb +25 -0
  38. data/lib/resilience/page.rb +104 -0
  39. data/lib/resilience/tables.rb +1 -0
  40. data/lib/resilience/tables/boot.rb +89 -0
  41. data/lib/resilience/tables/object.rb +6 -2
  42. data/lib/resilience/tables/system.rb +5 -2
  43. data/lib/resilience/trees.rb +5 -0
  44. data/lib/resilience/trees/object_tree.rb +45 -0
  45. metadata +55 -15
  46. data/bin/cle.rb +0 -24
  47. data/bin/clum.py +0 -28
  48. data/bin/fs.rb +0 -21
  49. data/bin/ref.rb +0 -49
  50. data/bin/rels.rb +0 -269
  51. data/bin/resilience.rb +0 -351
@@ -2,9 +2,9 @@
2
2
  # ReFS Constants
3
3
  # Copyright (C) 2015 Red Hat Inc.
4
4
 
5
- FIRST_PAGE_ID = 0x1e
5
+ FS_SIGNATURE = [0x00, 0x00, 0x00, 0x52, 0x65, 0x46, 0x53, 0x00] # ...ReFS.
6
+
6
7
  PAGE_SIZE = 0x4000
7
- FIRST_PAGE_ADDRESS = FIRST_PAGE_ID * PAGE_SIZE
8
8
 
9
9
  ROOT_DIR_ID = [0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0]
10
10
  DIR_ENTRY = 0x20030
@@ -14,19 +14,43 @@ DIR_TREE = 0x301
14
14
  DIR_LIST = 0x200
15
15
  #DIR_BRANCH = 0x000 ?
16
16
 
17
+ PAGES = {
18
+ # page id's:
19
+ :first => 0x1e,
20
+
21
+ # virtual page numbers:
22
+ :root => 0x00,
23
+ :object_table => 0x02,
24
+ :object_tree => 0x03
25
+ }
26
+
17
27
  ADDRESSES = {
28
+ # size / bounds
29
+ :bytes_per_sector => 0x20,
30
+ :sectors_per_cluster => 0x24,
31
+
18
32
  # page
33
+ :page_sequence => 0x08, # shadow pages share the same virtual page number
19
34
  :virtual_page_number => 0x18,
20
35
  :first_attr => 0x30,
21
36
 
22
- # page 0x1e
37
+ # on page 0x1e:
23
38
  :system_table_page => 0xA0,
24
39
 
25
- # system table
40
+ # on system table:
26
41
  :system_pages => 0x58,
27
42
 
28
- # generic table
29
- :num_objects => 0x20, # referenced from start of first attr
43
+ # generic table:
44
+ # referenced from start of first attr
45
+ :object_id => 0x0C,
46
+ :num_objects => 0x20,
47
+
48
+ # referenced from start of table header
49
+ :table_length => 0x04,
30
50
 
31
- :table_length => 0x04 # referenced from start of table header
51
+ # object tree:
52
+ :object_tree_start1 => 0x10,
53
+ :object_tree_end1 => 0x1F,
54
+ :object_tree_start2 => 0x20,
55
+ :object_tree_end2 => 0x2F
32
56
  }
@@ -0,0 +1,39 @@
1
+ # Resilience Core Ruby Extensions
2
+ #
3
+ # Licensed under the MIT license
4
+ # Copyright (C) 2015 Red Hat, Inc.
5
+
6
+ class String
7
+ def indented(places=1)
8
+ rjust(places, '0').upcase
9
+ end
10
+ end
11
+
12
+ class Fixnum
13
+ def indented(places=1)
14
+ '0x' + to_s(16).rjust(places, '0').upcase
15
+ end
16
+
17
+ def big_endian_str
18
+ [self].big_endian_str
19
+ end
20
+ end
21
+
22
+ class NilClass
23
+ def indented(places=1)
24
+ '0x' + ('0' * places)
25
+ end
26
+ end
27
+
28
+ class Array
29
+ def big_endian_str
30
+ str = '0x'
31
+ value = false
32
+ reverse_each { |b|
33
+ next if b == 0 && !value
34
+ value = true
35
+ str += b.to_s(16)
36
+ }
37
+ str
38
+ end
39
+ end
@@ -4,3 +4,6 @@
4
4
 
5
5
  require 'resilience/fs_dir/dir_base'
6
6
  require 'resilience/fs_dir/record'
7
+
8
+ require 'resilience/fs_dir/dir_entry'
9
+ require 'resilience/fs_dir/file_entry'
@@ -3,6 +3,8 @@
3
3
  # Copyright (C) 2014-2015 Red Hat Inc.
4
4
 
5
5
  require 'fileutils'
6
+ require 'resilience/collections/dirs'
7
+ require 'resilience/collections/files'
6
8
 
7
9
  module Resilience
8
10
  module FSDir
@@ -14,8 +16,8 @@ module Resilience
14
16
 
15
17
  def parse_dir_obj(object_id, prefix)
16
18
  object_table = image.object_table
17
- @dirs ||= {}
18
- @files ||= {}
19
+ @dirs ||= Dirs.new
20
+ @files ||= Files.new
19
21
 
20
22
  page_id = object_table.pages[object_id]
21
23
  page_address = page_id * PAGE_SIZE
@@ -90,15 +92,23 @@ module Resilience
90
92
  if entry_type == DIR_ENTRY
91
93
  dir_name = key_bytes[4..-1].pack('L*')
92
94
  dir_obj = value.unpack('C*')[0...8]
93
- dirs["#{prefix}\\#{dir_name}"] = dir_obj
95
+ dirs << DirEntry.new(:prefix => prefix,
96
+ :name => dir_name,
97
+ :metadata => dir_obj,
98
+ :record => record)
94
99
 
95
100
  dir_obj = [0, 0, 0, 0, 0, 0, 0, 0].concat(dir_obj)
96
101
  parse_dir_obj(dir_obj, "#{prefix}\\#{dir_name}")
97
102
 
98
103
  elsif entry_type == FILE_ENTRY
99
- file_name = key_bytes[4..-1].pack('L*')
100
- prefixed = "#{prefix}\\#{file_name}"
101
- files[prefixed] = value
104
+ filename = key_bytes[4..-1]
105
+ filename.delete(0)
106
+ filename = filename.pack('C*')
107
+
108
+ files << FileEntry.new(:prefix => prefix,
109
+ :name => filename,
110
+ :metadata => value,
111
+ :record => record)
102
112
  end
103
113
  end
104
114
  end # class DirBase
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/ruby
2
+ # ReFS Directory Dir Entry
3
+ # Copyright (C) 2014-2015 Red Hat Inc.
4
+
5
+ require 'fileutils'
6
+
7
+ module Resilience
8
+ module FSDir
9
+ class DirEntry
10
+ attr_accessor :prefix
11
+ attr_accessor :name
12
+ attr_accessor :metadata
13
+
14
+ # metadata record
15
+ attr_accessor :record
16
+
17
+ def initialize(args={})
18
+ @prefix = args[:prefix]
19
+ @name = args[:name]
20
+ @metadata = args[:metadata]
21
+ @record = args[:record]
22
+ end
23
+
24
+ def fullname
25
+ "#{prefix}\\#{name}"
26
+ end
27
+
28
+ def disk_offset
29
+ image.offset + dir.record.attribute.pos
30
+ end
31
+ end # class DirEntry
32
+ end # module FSDir
33
+ end # module Resilience
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/ruby
2
+ # ReFS Directory File Entry
3
+ # Copyright (C) 2014-2015 Red Hat Inc.
4
+
5
+ require 'fileutils'
6
+
7
+ module Resilience
8
+ module FSDir
9
+ class FileEntry
10
+ attr_accessor :prefix
11
+ attr_accessor :name
12
+ attr_accessor :metadata
13
+
14
+ # metadata record
15
+ attr_accessor :record
16
+
17
+ # known metadata attributes
18
+ attr_accessor :metadata_attrs
19
+
20
+ def initialize(args={})
21
+ @prefix = args[:prefix]
22
+ @name = args[:name]
23
+ @metadata = args[:metadata]
24
+ @record = args[:record]
25
+ parse_attrs
26
+ end
27
+
28
+ def fullname
29
+ "#{prefix}\\#{name}"
30
+ end
31
+
32
+ def total_offset
33
+ image.offset + record.attribute.pos
34
+ end
35
+
36
+ def parse_attrs
37
+ metadata_bytes = @metadata.unpack('C*')
38
+ metadata_dwords = @metadata.unpack('L*')
39
+ attr1_length = metadata_dwords[0]
40
+ attr1_dwords = attr1_length/4
41
+ attr2_length = metadata_dwords[attr1_dwords]
42
+ attr2_dwords = attr2_length/4
43
+ attr3_length = metadata_dwords[attr1_dwords + attr2_dwords]
44
+ # there may be other attrs after this point...
45
+
46
+ attr1 = metadata_bytes[0..attr1_length]
47
+ attr2 = metadata_bytes[attr1_length..attr1_length+attr2_length]
48
+ attr3 = metadata_bytes[attr1_length+attr2_length..attr1_length+attr2_length+attr3_length]
49
+ @metadata_attrs = [attr1, attr2, attr3]
50
+ end
51
+ end # class FileEntry
52
+ end # module FSDir
53
+ end # module Resilience
@@ -4,22 +4,41 @@
4
4
 
5
5
  module Resilience
6
6
  class Image
7
+ include Conf
8
+
7
9
  attr_accessor :file
8
10
  attr_accessor :offset
9
11
  attr_accessor :opts
10
12
 
13
+ attr_accessor :bytes_per_sector
14
+ attr_accessor :sectors_per_cluster
15
+
16
+ def cluster_size
17
+ @cluster_size ||= bytes_per_sector * sectors_per_cluster
18
+ end
19
+
11
20
  attr_accessor :root_dir
12
21
  attr_accessor :system_table
13
22
  attr_accessor :object_table
23
+ attr_accessor :object_tree
24
+
25
+ # all pages including shadow pages
26
+ attr_accessor :pages
14
27
 
15
28
  def initialize(args={})
16
29
  @file = args[:file] || []
17
30
  end
18
31
 
19
32
  def parse
33
+ parse_bounds
34
+
35
+ # each of these is a seperate parsing process,
36
+ # though later ones may depend on former
37
+ @pages = Resilience::Page.extract_all if conf.pages?
20
38
  @system_table = Resilience::SystemTable.parse
21
39
  @object_table = Resilience::ObjectTable.parse
22
40
  @root_dir = Resilience::RootDir.parse
41
+ @object_tree = Resilience::ObjectTree.parse if conf.object_tree?
23
42
  end
24
43
 
25
44
  def seek(position)
@@ -30,8 +49,22 @@ module Resilience
30
49
  @file.pos - offset
31
50
  end
32
51
 
52
+ def total_pos
53
+ @file.pos
54
+ end
55
+
33
56
  def read(len)
34
57
  @file.read(len)
35
58
  end
59
+
60
+ private
61
+
62
+ def parse_bounds
63
+ seek(ADDRESSES[:bytes_per_sector])
64
+ @bytes_per_sector = read(4).unpack('L').first
65
+
66
+ seek(ADDRESSES[:sectors_per_cluster])
67
+ @sectors_per_cluster = read(4).unpack('L').first
68
+ end
36
69
  end
37
70
  end # module Resilience
@@ -12,14 +12,39 @@ module Resilience
12
12
  @image ||= Resilience::Image.new
13
13
  end
14
14
 
15
+ def self.store_pos
16
+ @image_pos ||= []
17
+ @image_pos.unshift image.pos
18
+ end
19
+
20
+ def self.restore_pos
21
+ image.seek @image_pos.shift
22
+ end
23
+
15
24
  def image
16
25
  OnImage.image
17
26
  end
18
27
 
28
+ def store_pos
29
+ OnImage.store_pos
30
+ end
31
+
32
+ def restore_pos
33
+ OnImage.restore_pos
34
+ end
35
+
19
36
  module ClassMethods
20
37
  def image
21
38
  OnImage.image
22
39
  end
40
+
41
+ def store_pos
42
+ OnImage.store_pos
43
+ end
44
+
45
+ def restore_pos
46
+ OnImage.restore_pos
47
+ end
23
48
  end
24
49
  end # module OnImage
25
50
  end # module Resilience
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/ruby
2
+ # ReFS Page Representation
3
+ # Copyright (C) 2015 Red Hat Inc.
4
+
5
+ require 'resilience/collections/pages'
6
+
7
+ module Resilience
8
+ class Page
9
+ include OnImage
10
+
11
+ attr_accessor :id
12
+ attr_accessor :contents
13
+
14
+ attr_accessor :sequence
15
+ attr_accessor :virtual_page_number
16
+
17
+ attr_accessor :attributes
18
+
19
+ attr_accessor :object_id
20
+ attr_accessor :entries
21
+
22
+ def initialize
23
+ @attributes ||= []
24
+ end
25
+
26
+ def self.extract_all
27
+ page_id = PAGES[:first]
28
+ pages = Pages.new
29
+
30
+ image.seek(page_id * PAGE_SIZE)
31
+ while contents = image.read(PAGE_SIZE)
32
+ # only pull out metadata pages currently
33
+ extracted_id = id_from_contents(contents)
34
+ is_metadata = extracted_id == page_id
35
+ pages[page_id] = Page.parse(page_id, contents) if is_metadata
36
+ page_id += 1
37
+ end
38
+
39
+ pages
40
+ end
41
+
42
+ def self.id_from_contents(contents)
43
+ contents.unpack('S').first
44
+ end
45
+
46
+ def offset
47
+ id * PAGE_SIZE
48
+ end
49
+
50
+ def attr_start
51
+ offset + ADDRESSES[:first_attr]
52
+ end
53
+
54
+ def root?
55
+ virtual_page_number == PAGES[:root]
56
+ end
57
+
58
+ def object_table?
59
+ virtual_page_number == PAGES[:object_table]
60
+ end
61
+
62
+ def self.parse(id, contents)
63
+ store_pos
64
+
65
+ page = new
66
+ page.id = id
67
+ page.contents = contents
68
+
69
+ image.seek(page.offset + ADDRESSES[:page_sequence])
70
+ page.sequence = image.read(4).unpack('L').first
71
+
72
+ image.seek(page.offset + ADDRESSES[:virtual_page_number])
73
+ page.virtual_page_number = image.read(4).unpack('L').first
74
+
75
+ unless page.root? || page.object_table?
76
+ # TODO:
77
+ #page.parse_attributes
78
+ #page.parse_metadata
79
+ end
80
+
81
+ restore_pos
82
+
83
+ page
84
+ end
85
+
86
+ def has_attributes?
87
+ !@attributes.nil? && !@attributes.empty?
88
+ end
89
+
90
+ def parse_attributes
91
+ image.seek(attr_start)
92
+ while true
93
+ attr = Attribute.read
94
+ break if attr.empty?
95
+ @attributes << attr
96
+ end
97
+ end
98
+
99
+ def parse_metadata
100
+ @object_id = @attributes.first.unpack("C*")[ADDRESSES[:object_id]]
101
+ @entries = @attributes.first.unpack("C*")[ADDRESSES[:num_objects]]
102
+ end
103
+ end # class Page
104
+ end # module Resilience