package com.lightboxtechnologies.spectrum; import java.io.IOException; import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Hex; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.LongWritable; import org.sleuthkit.hadoop.core.SKMapper; public class ImportMetadataMapper extends SKMapper<Object, Text, LongWritable, JsonWritable> { private final Log LOG = LogFactory.getLog(ImportMetadataMapper.class.getName()); private final FsEntry Entry = new FsEntry(); private final byte[] Id = new byte[FsEntryUtils.ID_LENGTH]; private final MessageDigest Hasher = FsEntryUtils.getHashInstance("MD5"); private HTable Table; protected final List<Map<String,Object>> Extents = new ArrayList<Map<String,Object>>(); protected final Map<String,Object> Output = new HashMap<String,Object>(); protected final LongWritable Offset = new LongWritable(); protected final JsonWritable JsonOutput = new JsonWritable(); @Override public void setup(Context context) throws IOException { super.setup(context); final Configuration conf = context.getConfiguration(); Table = new HTable(conf, HBaseTables.ENTRIES_TBL_B); // ensure that the extents list is put into the output map Output.put("extents", Extents); } @Override protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { // read the JSON produced by fsrip if (!Entry.parseJson(value.toString())) { LOG.warn("JSON parse failed: " + value.toString()); return; } FsEntryUtils.makeFsEntryKey( Id, getImageID(), ((String) Entry.get("path")).getBytes(), (Integer) Entry.get("dirIndex"), Hasher ); // extract the extents data // TODO: This try/catch is nasty, fix it so we check return values // instead of throwing on bad input. try { final List<Map> attrs = (List<Map>) Entry.get("attrs"); if (attrs == null) { LOG.info(Entry.fullPath() + " has no attributes"); return; } long fsByteOffset = ((Number) Entry.get("fs_byte_offset")).longValue(); long fsBlockSize = ((Number) Entry.get("fs_block_size")).longValue(); for (final Map attribute : attrs) { final long flags = ((Number) attribute.get("flags")).longValue(); final long type = ((Number) attribute.get("type")).longValue(); if ((flags & 0x03) > 0 && (type & 0x81) > 0) { // flags & type indicate this attribute has nonresident data final Object nrds = attribute.get("nrd_runs"); if (nrds == null) { LOG.warn(Entry.fullPath() + " had an nrd attr with null runs"); return; } final List<Map<String, Object>> runs = (List<Map<String,Object>>)nrds; if (setExtents(runs, fsByteOffset, fsBlockSize) && !Extents.isEmpty()) { Entry.put("extents", Extents); Offset.set(((Number)Extents.get(0).get("addr")).longValue()); Output.put("size", Entry.get("size")); Output.put("fp", Entry.fullPath()); Output.put("id", Hex.encodeHexString(Id)); JsonOutput.set(Output); LOG.info(JsonOutput.toString()); context.write(Offset, JsonOutput); break; // take the first one we get... FIXME someday } } } } catch (ClassCastException e) { LOG.warn(errorString(e)); e.printStackTrace(); } catch (NullPointerException e) { LOG.warn(errorString(e)); e.printStackTrace(); } catch (Exception e) { LOG.warn(errorString(e)); e.printStackTrace(); } // write the metadata to HBase final Put p = FsEntryPut.create(Id, Entry, HBaseTables.ENTRIES_COLFAM_B); if (p.isEmpty()) { // TODO: inidicate failure here? return; } Table.put(p); } protected boolean setExtents(List<Map<String,Object>> dataRuns, long fsByteOffset, long fsBlockSize) { Extents.clear(); if (dataRuns.isEmpty()) { return false; } for (Map<String,Object> run: dataRuns) { Map<String,Object> dr = new HashMap<String,Object>(); dr.put("flags", run.get("flags")); dr.put("addr", (((Number)run.get("addr")).longValue() * fsBlockSize) + fsByteOffset); dr.put("offset", ((Number)run.get("offset")).longValue() * fsBlockSize); dr.put("len", ((Number)run.get("len")).longValue() * fsBlockSize); Extents.add(dr); } return true; } protected String errorString(Exception e) { final StringBuilder b = new StringBuilder("Exception on "); b.append(Hex.encodeHexString(Id)); b.append(":"); b.append(Entry.fullPath()); b.append(": "); try { b.append(Entry.toString()); } catch (NullPointerException npe) { // FIXME: bugger, this happens becuase FsEntry violates the Map contract b.append("null"); } return b.toString(); } }