Method: PEROBS::FlatFileBlobHeader.read

Defined in:
lib/perobs/FlatFileBlobHeader.rb

.read(file, addr = nil, id = nil) ⇒ Object

Read the header from the given File.

Parameters:

  • file (File)
  • addr (Integer) (defaults to: nil)

    address in the file to start reading. If no address is specified use the current position in the file.

  • id (Integer) (defaults to: nil)

    Optional ID that the header should have. If no id is specified there is no check against the actual ID done.

Returns:

  • FlatFileBlobHeader or nil if there are no more blobs to read in the file.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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
186
187
188
189
190
191
192
193
194
# File 'lib/perobs/FlatFileBlobHeader.rb', line 85

def FlatFileBlobHeader::read(file, addr = nil, id = nil)
  # If an address was specified we expect the read to always succeed. If
  # no address is specified and we can't read the header we generate an
  # error message but it is not fatal.
  errors_are_fatal = !addr.nil?

  mode = :searching_next_header
  addr = file.pos unless addr
  buf = nil
  corruption_start = nil

  loop do
    buf_with_crc = nil
    begin
      file.seek(addr)
      buf_with_crc = file.read(LENGTH)
    rescue IOError => e
      if errors_are_fatal
        PEROBS.log.fatal "Cannot read blob header in flat file DB at " +
          "address #{addr}: #{e.message}"
      else
        PEROBS.log.error "Cannot read blob header in flat file DB: " +
          e.message
        return nil
      end
    end

    # Did we read anything?
    if buf_with_crc.nil?
      if errors_are_fatal
        PEROBS.log.fatal "Cannot read blob header " +
          "#{id ? "for ID #{id} " : ''}at address #{addr}"
      else
        if corruption_start
          PEROBS.log.error "Corruption found at end of blob file at " +
            "address #{addr}"
        end
        # We have reached the end of the file.
        return nil
      end
    end

    # Did we get the full header?
    if buf_with_crc.length != LENGTH
      msg = "Incomplete FlatFileBlobHeader: Only " +
        "#{buf_with_crc.length} " +
        "bytes of #{LENGTH} could be read "
      "#{id ? "for ID #{id} " : ''}at address #{addr}"
      if errors_are_fatal
        PEROBS.log.fatal msg
      else
        PEROBS.log.error msg
      end
      return nil
    end

    # Check the CRC of the header
    buf = buf_with_crc[0..-5]
    crc = buf_with_crc[-4..-1].unpack('L')[0]

    if (read_crc = Zlib.crc32(buf, 0)) == crc
      # We have found a valid header.
      if corruption_start
        PEROBS.log.error "FlatFile corruption ends at #{addr}. " +
          "#{addr - corruption_start} bytes skipped. Some data may " +
          "not be recoverable."
      end
      break
    else
      if errors_are_fatal
        PEROBS.log.fatal "FlatFile Header CRC mismatch at address " +
          "#{addr}. Header CRC is #{'%08x' % read_crc} but should be " +
          "#{'%08x' % crc}."
      else
        if corruption_start.nil?
          if errors_are_fatal
            PEROBS.log.fatal "FlatFile corruption found. The FlatFile " +
              "Header CRC mismatch at address #{addr}. Header CRC is " +
              "#{'%08x' % read_crc} but should be #{'%08x' % crc}."
          else
            PEROBS.log.error "FlatFile corruption found. The FlatFile " +
              "Header CRC mismatch at address #{addr}. Header CRC is " +
              "#{'%08x' % read_crc} but should be #{'%08x' % crc}. " +
              "Trying to find the next header."
          end
          corruption_start = addr
        end
        # The blob file is corrupted. There is no valid header at the
        # current position in the file. We now try to find the next valid
        # header by iterating over the remainder of the file advanding one
        # byte with each step until we hit the end of the file or find the
        # next valid header.
        addr += 1
      end
    end
  end

  header = FlatFileBlobHeader.new(file, addr, *buf.unpack(FORMAT))
  if corruption_start
    header.corruption_start = corruption_start
  end

  if id && header.id != id
    PEROBS.log.fatal "Mismatch between FlatFile index and blob file " +
      "found. FlatFile has entry with ID #{header.id} at address " +
      "#{addr}. Index has ID #{id} for this address."
  end

  return header
end