Class: NTFS::IndexRoot
- Inherits:
-
Object
- Object
- NTFS::IndexRoot
- Defined in:
- lib/fs/ntfs/attrib_index_root.rb
Constant Summary collapse
- DEBUG_TRACE_FIND =
false && $log
- CT_BINARY =
Binary compare, MSB is first (does that mean big endian?)
0x00000000
- CT_FILENAME =
UNICODE strings.
0x00000001
- CT_UNICODE =
UNICODE, upper case first.
0x00000002
- CT_ULONG =
Standard ULONG, 32-bits little endian.
0x00000010
- CT_SID =
Security identifier.
0x00000011
- CT_SECHASH =
First security hash, then security identifier.
0x00000012
- CT_ULONGS =
Set of ULONGS? (doc is unclear - indicates GUID).
0x00000013
Instance Attribute Summary collapse
-
#index ⇒ Object
readonly
Returns the value of attribute index.
-
#indexAlloc ⇒ Object
readonly
Returns the value of attribute indexAlloc.
-
#nodeHeader ⇒ Object
readonly
Returns the value of attribute nodeHeader.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Class Method Summary collapse
Instance Method Summary collapse
- #allocations=(indexAllocations) ⇒ Object
- #bitmap=(bmp) ⇒ Object
- #cleanAllocEntries(entries) ⇒ Object
- #dump ⇒ Object
-
#find(name) ⇒ Object
Find a name in this index.
- #findBackup(name) ⇒ Object
- #findInEntries(name, entries) ⇒ Object
- #getIndexAllocEntries(vcn) ⇒ Object
- #globAllEntries(entries) ⇒ Object
- #globEntries ⇒ Object
- #globEntriesByName ⇒ Object
-
#globNames ⇒ Object
Return all names in this index as a sorted string array.
-
#initialize(buf, boot_sector) ⇒ IndexRoot
constructor
A new instance of IndexRoot.
- #to_s ⇒ Object
Constructor Details
#initialize(buf, boot_sector) ⇒ IndexRoot
Returns a new instance of IndexRoot.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 62 def initialize(buf, boot_sector) log_prefix = "MIQ(NTFS::IndexRoot.initialize)" raise "#{log_prefix} Nil buffer" if buf.nil? raise "#{log_prefix} Nil boot sector" if boot_sector.nil? buf = buf.read(buf.length) if buf.kind_of?(DataRun) @air = ATTRIB_INDEX_ROOT.decode(buf) buf = buf[SIZEOF_ATTRIB_INDEX_ROOT..-1] # Get accessor data. @type = @air['type'] @collation_rule = @air['collation_rule'] @byteSize = @air['index_block_size'] @clusterSize = @air['size_of_index_clus'] @boot_sector = boot_sector # Get node header & index. @foundEntries = {} @indexNodeHeader = IndexNodeHeader.new(buf) @indexEntries = cleanAllocEntries(DirectoryIndexNode.nodeFactory(buf[@indexNodeHeader.startEntries..-1])) @indexAlloc = {} end |
Instance Attribute Details
#index ⇒ Object (readonly)
Returns the value of attribute index.
60 61 62 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 60 def index @index end |
#indexAlloc ⇒ Object (readonly)
Returns the value of attribute indexAlloc.
60 61 62 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 60 def indexAlloc @indexAlloc end |
#nodeHeader ⇒ Object (readonly)
Returns the value of attribute nodeHeader.
60 61 62 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 60 def nodeHeader @nodeHeader end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
60 61 62 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 60 def type @type end |
Class Method Details
.create_from_header(header, buf, bs) ⇒ Object
54 55 56 57 58 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 54 def self.create_from_header(header, buf, bs) return IndexRoot.new(buf, bs) if header.containsFileNameIndexes? $log.debug("Skipping #{header.typeName} for name <#{header.name}>") if $log nil end |
Instance Method Details
#allocations=(indexAllocations) ⇒ Object
91 92 93 94 95 96 97 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 91 def allocations=(indexAllocations) @indexAllocRuns = [] if @indexNodeHeader.hasChildren? && indexAllocations indexAllocations.each { |alloc| @indexAllocRuns << [alloc.header, alloc.data_run] } end @indexAllocRuns end |
#bitmap=(bmp) ⇒ Object
99 100 101 102 103 104 105 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 99 def bitmap=(bmp) if @indexNodeHeader.hasChildren? @bitmap = bmp.data.unpack("b#{bmp.length * 8}") unless bmp.nil? end @bitmap end |
#cleanAllocEntries(entries) ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 187 def cleanAllocEntries(entries) cleanEntries = [] entries.each do |e| if e.isLast? || !(e.contentLen == 0 || (e.refMft[1] < 12 && e.name[0, 1] == "$")) cleanEntries << e # Since we are already looping through all entries to clean # them we can store them in a lookup for optimization @foundEntries[e.name.downcase] = e unless e.isLast? end end cleanEntries end |
#dump ⇒ Object
238 239 240 241 242 243 244 245 246 247 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 238 def dump out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n" out << " Type : 0x#{'%08x' % @type}\n" out << " Collation Rule : #{@collation_rule}\n" out << " Index size (bytes) : #{@byteSize}\n" out << " Index size (clusters): #{@clusterSize}\n" @indexEntries.each { |din| out << din.dump } out << "---\n" out end |
#find(name) ⇒ Object
Find a name in this index.
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 108 def find(name) log_prefix = "MIQ(NTFS::IndexRoot.find)" name = name.downcase $log.debug "#{log_prefix} Searching for [#{name}]" if DEBUG_TRACE_FIND if @foundEntries.key?(name) $log.debug "#{log_prefix} Found [#{name}] (cached)" if DEBUG_TRACE_FIND return @foundEntries[name] end found = findInEntries(name, @indexEntries) if found.nil? # Fallback to full directory search if not found $log.debug "#{log_prefix} [#{name}] not found. Performing full directory scan." if $log found = findBackup(name) $log.send(found.nil? ? :debug : :warn, "#{log_prefix} [#{name}] #{found.nil? ? "not " : ""}found in full directory scan.") if $log end found end |
#findBackup(name) ⇒ Object
155 156 157 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 155 def findBackup(name) globEntriesByName[name] end |
#findInEntries(name, entries) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 134 def findInEntries(name, entries) log_prefix = "MIQ(NTFS::IndexRoot.findInEntries)" if @foundEntries.key?(name) $log.debug "#{log_prefix} Found [#{name}] in #{entries.collect { |e| e.isLast? ? "**last**" : e.name.downcase }.inspect}" if DEBUG_TRACE_FIND return @foundEntries[name] end $log.debug "#{log_prefix} Searching for [#{name}] in #{entries.collect { |e| e.isLast? ? "**last**" : e.name.downcase }.inspect}" if DEBUG_TRACE_FIND # TODO: Uses linear search within an index entry; switch to more performant search eventually entries.each do |e| $log.debug "#{log_prefix} before [#{e.isLast? ? "**last**" : e.name.downcase}]" if DEBUG_TRACE_FIND if e.isLast? || name < e.name.downcase $log.debug "#{log_prefix} #{e.hasChild? ? "Sub-search in child vcn [#{e.child}]" : "No sub-search"}" if DEBUG_TRACE_FIND return e.hasChild? ? findInEntries(name, getIndexAllocEntries(e.child)) : nil end end $log.debug "#{log_prefix} Did not find [#{name}]" if DEBUG_TRACE_FIND nil end |
#getIndexAllocEntries(vcn) ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 159 def getIndexAllocEntries(vcn) unless @indexAlloc.key?(vcn) log_prefix = "MIQ(NTFS::IndexRoot.getIndexAllocEntries)" begin raise "not allocated" if @bitmap[vcn, 1] == "0" header, run = @indexAllocRuns.detect { |h, _r| vcn >= h.specific['first_vcn'] && vcn <= h.specific['last_vcn'] } raise "header not found" if header.nil? raise "run not found" if run.nil? run.seekToVcn(vcn - header.specific['first_vcn']) buf = run.read(@byteSize) raise "buffer not found" if buf.nil? raise "buffer signature is expected to be INDX, but is [#{buf[0, 4].inspect}]" if buf[0, 4] != "INDX" irh = IndexRecordHeader.new(buf, @boot_sector.bytesPerSector) buf = irh.data[IndexRecordHeader.size..-1] inh = IndexNodeHeader.new(buf) @indexAlloc[vcn] = cleanAllocEntries(DirectoryIndexNode.nodeFactory(buf[inh.startEntries..-1])) rescue => err $log.warn "#{log_prefix} Unable to read data from index allocation at vcn [#{vcn}] because <#{err.}>\n#{dump}" if $log @indexAlloc[vcn] = [] end end @indexAlloc[vcn] end |
#globAllEntries(entries) ⇒ Object
229 230 231 232 233 234 235 236 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 229 def globAllEntries(entries) ret = [] entries.each do |e| ret += globAllEntries(getIndexAllocEntries(e.child)) if e.hasChild? ret << e unless e.isLast? end ret end |
#globEntries ⇒ Object
200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 200 def globEntries return @globEntries unless @globEntries.nil? # Since we are reading all entries, retrieve all of the data in one call @indexAllocRuns.each do |_h, r| r.rewind r.read(r.length) end @globEntries = globAllEntries(@indexEntries) end |
#globEntriesByName ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 212 def globEntriesByName log_prefix = "MIQ(NTFS::IndexRoot.globEntriesByName)" if @globbedEntriesByName $log.debug "#{log_prefix} Using cached globEntries." if DEBUG_TRACE_FIND return @foundEntries end $log.debug "#{log_prefix} Initializing globEntries:" if DEBUG_TRACE_FIND globEntries.each do |e| $log.debug "#{log_prefix} #{e.isLast? ? "**last**" : e.name.downcase}" if DEBUG_TRACE_FIND @foundEntries[e.name.downcase] = e end @globbedEntriesByName = true @foundEntries end |
#globNames ⇒ Object
Return all names in this index as a sorted string array.
129 130 131 132 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 129 def globNames @globNames = globEntries.collect { |e| e.namespace == NTFS::FileName::NS_DOS ? nil : e.name.downcase }.compact if @globNames.nil? @globNames end |
#to_s ⇒ Object
87 88 89 |
# File 'lib/fs/ntfs/attrib_index_root.rb', line 87 def to_s @type.to_s end |