require 'binary_struct' require 'fs/ntfs/attrib_file_name' require 'fs/ntfs/utils' module NTFS DIR_INDEX_NODE = BinaryStruct.new([ 'Q', 'mft_ref', # MFT file reference for file name (goofy ref). 'S', 'length', # Length of entry. 'S', 'content_len', # Length of $FILE_NAME attrib 'L', 'flags', # See IN_ below (note: these will eventually become general flags) ]) # Here follows a $FILE_NAME attribute if content_len > 0. # Last 8 bytes starting on 8 byte boundary are the VCN of the child node in $INDEX_ALLOCATION (if IN_HAS_CHILD is set). SIZEOF_DIR_INDEX_NODE = DIR_INDEX_NODE.size class DirectoryIndexNode IN_HAS_CHILD = 0x00000001 IN_LAST_ENTRY = 0x00000002 attr_reader :refMft, :length, :contentLen, :flags, :child, :afn, :mftEntry def self.nodeFactory(buf) nodes = [] loop do node = DirectoryIndexNode.new(buf) buf = buf[node.length..-1] nodes << node break if node.isLast? end nodes end def initialize(buf) raise "MIQ(NTFS::DirectoryIndexNode.initialize) Nil buffer" if buf.nil? buf = buf.read(buf.length) if buf.kind_of?(DataRun) # Decode the directory index node structure. @din = DIR_INDEX_NODE.decode(buf) # Get accessor data. @mftEntry = nil @refMft = NTFS::Utils.MkRef(@din['mft_ref']) @length = @din['length'] @contentLen = @din['content_len'] @flags = @din['flags'] # If there's a $FILE_NAME attrib get it. @afn = FileName.new(buf[SIZEOF_DIR_INDEX_NODE, buf.size]) if @contentLen > 0 # If there's a child node VCN get it. if NTFS::Utils.gotBit?(@flags, IN_HAS_CHILD) # Child node VCN is located 8 bytes before 'length' bytes. # NOTE: If the node has 0 contents, it's offset 16. @child = buf[@contentLen == 0 ? 16 : @length - 8, 8].unpack('Q')[0] end end # String rep. def to_s "\#<#{self.class}:0x#{'%08x' % object_id} name='#{name}'>" end # Return file name (if resolved). def name @afn.nil? ? nil : @afn.name end # Return namespace. def namespace @afn.nil? ? nil : @afn.namespace end # Return true if has children. def hasChild? NTFS::Utils.gotBit?(@flags, IN_HAS_CHILD) end # Return true if this is the last entry. def isLast? NTFS::Utils.gotBit?(@flags, IN_LAST_ENTRY) end # If content is 0, then obviously not a directory. def isDir? return false if @contentLen == 0 @mftEntry.isDir? end # Resolves this node's file reference. def resolve(bs) if @contentLen > 0 @mftEntry = bs.mftEntry(@refMft[1]) raise "MIQ(NTFS::DirectoryIndexNode.resolve) Stale reference: #{inspect}" if @refMft[0] != @mftEntry.sequenceNum end @mftEntry end # Dumps object. def dump out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n" out << " Mft Ref : seq #{@refMft[0]}, entry #{@refMft[1]}\n" out << " Length : #{@length}\n" out << " Content : #{@contentLen}\n" out << " Flags : 0x#{'%08x' % @flags}\n" out << @afn.dump if @contentLen > 0 out << " Child ref: #{@child}\n" if NTFS::Utils.gotBit?(@flags, IN_HAS_CHILD) out << "---\n" end end end # module NTFS