mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 01:37:43 +00:00
688 lines
20 KiB
Java
Executable File
688 lines
20 KiB
Java
Executable File
/*
|
|
* Autopsy Forensic Browser
|
|
*
|
|
* Copyright 2020 Basis Technology Corp.
|
|
* Contact: carrier <at> sleuthkit <dot> org
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package zookeepernodemigration;
|
|
|
|
import java.io.Serializable;
|
|
import java.nio.BufferUnderflowException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.Date;
|
|
import javax.lang.model.type.TypeKind;
|
|
|
|
/**
|
|
* An object that converts auto ingest job data for an auto ingest job
|
|
* coordination service node to and from byte arrays.
|
|
*/
|
|
final class AutoIngestJobNodeData {
|
|
|
|
private static final int CURRENT_VERSION = 2;
|
|
private static final int DEFAULT_PRIORITY = 0;
|
|
|
|
/*
|
|
* This number is the sum of each piece of data, based on it's type. For the
|
|
* types boolean, int, and long, values 1, 4, and 8 will be added
|
|
* respectively. For String objects, the length of the string, plus either a
|
|
* byte or short respesenting the length of the string, will be added.
|
|
*
|
|
* This field is used to set the size of the buffer during the byte array
|
|
* creation in the 'toArray()' method. Since the final size of the array
|
|
* isn't immediately known at the time of creation, this number is used to
|
|
* create an array as large as could possibly be needed to store all the
|
|
* data. This avoids the need to continuously enlarge the buffer. Once the
|
|
* buffer has all the necessary data, it will be resized as appropriate.
|
|
*/
|
|
private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131637;
|
|
|
|
/*
|
|
* Version 0 fields.
|
|
*/
|
|
private int processingStatus;
|
|
private int priority;
|
|
private int numberOfCrashes;
|
|
private long completedDate;
|
|
private boolean errorsOccurred;
|
|
|
|
/*
|
|
* Version 1 fields.
|
|
*/
|
|
private int version;
|
|
private String manifestFilePath; // 'short' length used in byte array
|
|
private long manifestFileDate;
|
|
private String caseName; // 'byte' length used in byte array
|
|
private String deviceId; // 'byte' length used in byte array
|
|
private String dataSourcePath; // 'short' length used in byte array
|
|
private String caseDirectoryPath; // 'short' length used in byte array
|
|
private String processingHostName; // 'short' length used in byte array
|
|
private byte processingStage;
|
|
private long processingStageStartDate;
|
|
private String processingStageDetailsDescription; // 'byte' length used in byte array
|
|
private long processingStageDetailsStartDate;
|
|
|
|
/*
|
|
* Version 2 fields.
|
|
*/
|
|
private long dataSourceSize;
|
|
|
|
/**
|
|
* Processing statuses for an auto ingest job.
|
|
*/
|
|
enum ProcessingStatus {
|
|
PENDING,
|
|
PROCESSING,
|
|
COMPLETED,
|
|
DELETED
|
|
}
|
|
|
|
/**
|
|
* Processing stages for an auto ingest job.
|
|
*/
|
|
enum Stage {
|
|
|
|
PENDING("Pending"),
|
|
STARTING("Starting"),
|
|
UPDATING_SHARED_CONFIG("Updating shared configuration"),
|
|
CHECKING_SERVICES("Checking services"),
|
|
OPENING_CASE("Opening case"),
|
|
IDENTIFYING_DATA_SOURCE("Identifying data source type"),
|
|
ADDING_DATA_SOURCE("Adding data source"),
|
|
ANALYZING_DATA_SOURCE("Analyzing data source"),
|
|
ANALYZING_FILES("Analyzing files"),
|
|
EXPORTING_FILES("Exporting files"),
|
|
CANCELLING_MODULE("Cancelling module"),
|
|
CANCELLING("Cancelling"),
|
|
COMPLETED("Completed");
|
|
|
|
private final String displayText;
|
|
|
|
private Stage(String displayText) {
|
|
this.displayText = displayText;
|
|
}
|
|
|
|
String getDisplayText() {
|
|
return displayText;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Processing stage details for an auto ingest job.
|
|
*/
|
|
static final class StageDetails implements Serializable {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private final String description;
|
|
private final Date startDate;
|
|
|
|
StageDetails(String description, Date startDate) {
|
|
this.description = description;
|
|
this.startDate = startDate;
|
|
}
|
|
|
|
String getDescription() {
|
|
return this.description;
|
|
}
|
|
|
|
Date getStartDate() {
|
|
return new Date(this.startDate.getTime());
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets the current version of the auto ingest job coordination service node
|
|
* data.
|
|
*
|
|
* @return The version number.
|
|
*/
|
|
static int getCurrentVersion() {
|
|
return AutoIngestJobNodeData.CURRENT_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Uses a coordination service node data to construct an object that
|
|
* converts auto ingest job data for an auto ingest job coordination service
|
|
* node to and from byte arrays.
|
|
*
|
|
* @param nodeData The raw bytes received from the coordination service.
|
|
*/
|
|
AutoIngestJobNodeData(byte[] nodeData) throws InvalidDataException {
|
|
if (null == nodeData || nodeData.length == 0) {
|
|
throw new InvalidDataException(null == nodeData ? "Null nodeData byte array" : "Zero-length nodeData byte array");
|
|
}
|
|
|
|
/*
|
|
* Set default values for all fields.
|
|
*/
|
|
this.processingStatus = ProcessingStatus.PENDING.ordinal();
|
|
this.priority = DEFAULT_PRIORITY;
|
|
this.numberOfCrashes = 0;
|
|
this.completedDate = 0L;
|
|
this.errorsOccurred = false;
|
|
this.version = 0;
|
|
this.manifestFilePath = "";
|
|
this.manifestFileDate = 0L;
|
|
this.caseName = "";
|
|
this.deviceId = "";
|
|
this.dataSourcePath = "";
|
|
this.caseDirectoryPath = "";
|
|
this.processingHostName = "";
|
|
this.processingStage = (byte) Stage.PENDING.ordinal();
|
|
this.processingStageStartDate = 0L;
|
|
this.processingStageDetailsDescription = "";
|
|
this.processingStageDetailsStartDate = 0L;
|
|
this.dataSourceSize = 0L;
|
|
|
|
/*
|
|
* Get fields from node data.
|
|
*/
|
|
ByteBuffer buffer = ByteBuffer.wrap(nodeData);
|
|
try {
|
|
if (buffer.hasRemaining()) {
|
|
/*
|
|
* Get version 0 fields.
|
|
*/
|
|
this.processingStatus = buffer.getInt();
|
|
this.priority = buffer.getInt();
|
|
this.numberOfCrashes = buffer.getInt();
|
|
this.completedDate = buffer.getLong();
|
|
int errorFlag = buffer.getInt();
|
|
this.errorsOccurred = (1 == errorFlag);
|
|
}
|
|
|
|
if (buffer.hasRemaining()) {
|
|
/*
|
|
* Get version 1 fields.
|
|
*/
|
|
this.version = buffer.getInt();
|
|
this.deviceId = getStringFromBuffer(buffer, TypeKind.BYTE);
|
|
this.caseName = getStringFromBuffer(buffer, TypeKind.BYTE);
|
|
this.caseDirectoryPath = getStringFromBuffer(buffer, TypeKind.SHORT);
|
|
this.manifestFileDate = buffer.getLong();
|
|
this.manifestFilePath = getStringFromBuffer(buffer, TypeKind.SHORT);
|
|
this.dataSourcePath = getStringFromBuffer(buffer, TypeKind.SHORT);
|
|
this.processingStage = buffer.get();
|
|
this.processingStageStartDate = buffer.getLong();
|
|
this.processingStageDetailsDescription = getStringFromBuffer(buffer, TypeKind.BYTE);
|
|
this.processingStageDetailsStartDate = buffer.getLong();
|
|
this.processingHostName = getStringFromBuffer(buffer, TypeKind.SHORT);
|
|
}
|
|
|
|
if (buffer.hasRemaining()) {
|
|
/*
|
|
* Get version 2 fields.
|
|
*/
|
|
this.dataSourceSize = buffer.getLong();
|
|
}
|
|
|
|
} catch (BufferUnderflowException ex) {
|
|
throw new InvalidDataException("Node data is incomplete", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the processing status of the job.
|
|
*
|
|
* @return The processing status.
|
|
*/
|
|
ProcessingStatus getProcessingStatus() {
|
|
return ProcessingStatus.values()[this.processingStatus];
|
|
}
|
|
|
|
/**
|
|
* Sets the processing status of the job.
|
|
*
|
|
* @param processingSatus The processing status.
|
|
*/
|
|
void setProcessingStatus(ProcessingStatus processingStatus) {
|
|
this.processingStatus = processingStatus.ordinal();
|
|
}
|
|
|
|
/**
|
|
* Gets the priority of the job.
|
|
*
|
|
* @return The priority.
|
|
*/
|
|
int getPriority() {
|
|
return this.priority;
|
|
}
|
|
|
|
/**
|
|
* Sets the priority of the job. A higher number indicates a higheer
|
|
* priority.
|
|
*
|
|
* @param priority The priority.
|
|
*/
|
|
void setPriority(int priority) {
|
|
this.priority = priority;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of times the job has crashed during processing.
|
|
*
|
|
* @return The number of crashes.
|
|
*/
|
|
int getNumberOfCrashes() {
|
|
return this.numberOfCrashes;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of times the job has crashed during processing.
|
|
*
|
|
* @param numberOfCrashes The number of crashes.
|
|
*/
|
|
void setNumberOfCrashes(int numberOfCrashes) {
|
|
this.numberOfCrashes = numberOfCrashes;
|
|
}
|
|
|
|
/**
|
|
* Gets the date the job was completed. A completion date equal to the epoch
|
|
* (January 1, 1970, 00:00:00 GMT), i.e., Date.getTime() returns 0L,
|
|
* indicates the job has not been completed.
|
|
*
|
|
* @return The job completion date.
|
|
*/
|
|
Date getCompletedDate() {
|
|
return new Date(this.completedDate);
|
|
}
|
|
|
|
/**
|
|
* Sets the date the job was completed. A completion date equal to the epoch
|
|
* (January 1, 1970, 00:00:00 GMT), i.e., Date.getTime() returns 0L,
|
|
* indicates the job has not been completed.
|
|
*
|
|
* @param completedDate The job completion date.
|
|
*/
|
|
void setCompletedDate(Date completedDate) {
|
|
this.completedDate = completedDate.getTime();
|
|
}
|
|
|
|
/**
|
|
* Gets whether or not any errors occurred during the processing of the job.
|
|
*
|
|
* @return True or false.
|
|
*/
|
|
boolean getErrorsOccurred() {
|
|
return this.errorsOccurred;
|
|
}
|
|
|
|
/**
|
|
* Sets whether or not any errors occurred during the processing of job.
|
|
*
|
|
* @param errorsOccurred True or false.
|
|
*/
|
|
void setErrorsOccurred(boolean errorsOccurred) {
|
|
this.errorsOccurred = errorsOccurred;
|
|
}
|
|
|
|
/**
|
|
* Gets the node data version number.
|
|
*
|
|
* @return The version number.
|
|
*/
|
|
int getVersion() {
|
|
return this.version;
|
|
}
|
|
|
|
/**
|
|
* Gets the device ID of the device associated with the data source for the
|
|
* job.
|
|
*
|
|
* @return The device ID.
|
|
*/
|
|
String getDeviceId() {
|
|
return this.deviceId;
|
|
}
|
|
|
|
/**
|
|
* Sets the device ID of the device associated with the data source for the
|
|
* job.
|
|
*
|
|
* @param deviceId The device ID.
|
|
*/
|
|
void setDeviceId(String deviceId) {
|
|
this.deviceId = deviceId;
|
|
}
|
|
|
|
/**
|
|
* Gets the case name.
|
|
*
|
|
* @return The case name.
|
|
*/
|
|
String getCaseName() {
|
|
return this.caseName;
|
|
}
|
|
|
|
/**
|
|
* Sets the case name.
|
|
*
|
|
* @param caseName The case name.
|
|
*/
|
|
void setCaseName(String caseName) {
|
|
this.caseName = caseName;
|
|
}
|
|
|
|
/**
|
|
* Sets the path to the case directory of the case associated with the job.
|
|
*
|
|
* @param caseDirectoryPath The path to the case directory.
|
|
*/
|
|
synchronized void setCaseDirectoryPath(Path caseDirectoryPath) {
|
|
if (caseDirectoryPath == null) {
|
|
this.caseDirectoryPath = "";
|
|
} else {
|
|
this.caseDirectoryPath = caseDirectoryPath.toString();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the path to the case directory of the case associated with the job.
|
|
*
|
|
* @return The case directory path or an empty string path if the case
|
|
* directory has not been created yet.
|
|
*/
|
|
synchronized Path getCaseDirectoryPath() {
|
|
if (!caseDirectoryPath.isEmpty()) {
|
|
return Paths.get(caseDirectoryPath);
|
|
} else {
|
|
return Paths.get("");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the date the manifest was created.
|
|
*
|
|
* @return The date the manifest was created.
|
|
*/
|
|
Date getManifestFileDate() {
|
|
return new Date(this.manifestFileDate);
|
|
}
|
|
|
|
/**
|
|
* Sets the date the manifest was created.
|
|
*
|
|
* @param manifestFileDate The date the manifest was created.
|
|
*/
|
|
void setManifestFileDate(Date manifestFileDate) {
|
|
this.manifestFileDate = manifestFileDate.getTime();
|
|
}
|
|
|
|
/**
|
|
* Gets the manifest file path.
|
|
*
|
|
* @return The manifest file path.
|
|
*/
|
|
Path getManifestFilePath() {
|
|
return Paths.get(this.manifestFilePath);
|
|
}
|
|
|
|
/**
|
|
* Sets the manifest file path.
|
|
*
|
|
* @param manifestFilePath The manifest file path.
|
|
*/
|
|
void setManifestFilePath(Path manifestFilePath) {
|
|
if (manifestFilePath != null) {
|
|
this.manifestFilePath = manifestFilePath.toString();
|
|
} else {
|
|
this.manifestFilePath = "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the path of the data source for the job.
|
|
*
|
|
* @return The data source path.
|
|
*/
|
|
Path getDataSourcePath() {
|
|
return Paths.get(dataSourcePath);
|
|
}
|
|
|
|
/**
|
|
* Get the file name portion of the path of the data source for the job.
|
|
*
|
|
* @return The data source file name.
|
|
*/
|
|
public String getDataSourceFileName() {
|
|
return Paths.get(dataSourcePath).getFileName().toString();
|
|
}
|
|
|
|
/**
|
|
* Sets the path of the data source for the job.
|
|
*
|
|
* @param dataSourcePath The data source path.
|
|
*/
|
|
void setDataSourcePath(Path dataSourcePath) {
|
|
if (dataSourcePath != null) {
|
|
this.dataSourcePath = dataSourcePath.toString();
|
|
} else {
|
|
this.dataSourcePath = "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the processing stage of the job.
|
|
*
|
|
* @return The processing stage.
|
|
*/
|
|
Stage getProcessingStage() {
|
|
return Stage.values()[this.processingStage];
|
|
}
|
|
|
|
/**
|
|
* Sets the processing stage job.
|
|
*
|
|
* @param processingStage The processing stage.
|
|
*/
|
|
void setProcessingStage(Stage processingStage) {
|
|
this.processingStage = (byte) processingStage.ordinal();
|
|
}
|
|
|
|
/**
|
|
* Gets the processing stage start date.
|
|
*
|
|
* @return The processing stage start date.
|
|
*/
|
|
Date getProcessingStageStartDate() {
|
|
return new Date(this.processingStageStartDate);
|
|
}
|
|
|
|
/**
|
|
* Sets the processing stage start date.
|
|
*
|
|
* @param processingStageStartDate The processing stage start date.
|
|
*/
|
|
void setProcessingStageStartDate(Date processingStageStartDate) {
|
|
this.processingStageStartDate = processingStageStartDate.getTime();
|
|
}
|
|
|
|
/**
|
|
* Get the processing stage details.
|
|
*
|
|
* @return A processing stage details object.
|
|
*/
|
|
StageDetails getProcessingStageDetails() {
|
|
return new StageDetails(this.processingStageDetailsDescription, new Date(this.processingStageDetailsStartDate));
|
|
}
|
|
|
|
/**
|
|
* Sets the details of the current processing stage.
|
|
*
|
|
* @param stageDetails A stage details object.
|
|
*/
|
|
void setProcessingStageDetails(StageDetails stageDetails) {
|
|
this.processingStageDetailsDescription = stageDetails.getDescription();
|
|
this.processingStageDetailsStartDate = stageDetails.getStartDate().getTime();
|
|
}
|
|
|
|
/**
|
|
* Gets the processing host name, may be the empty string.
|
|
*
|
|
* @return The processing host. The empty string if the job is not currently
|
|
* being processed.
|
|
*/
|
|
String getProcessingHostName() {
|
|
return this.processingHostName;
|
|
}
|
|
|
|
/**
|
|
* Sets the processing host name. May be the empty string.
|
|
*
|
|
* @param processingHost The processing host name. The empty string if the
|
|
* job is not currently being processed.
|
|
*/
|
|
void setProcessingHostName(String processingHost) {
|
|
this.processingHostName = processingHost;
|
|
}
|
|
|
|
/**
|
|
* Gets the total size of the data source.
|
|
*
|
|
* @return The data source size.
|
|
*/
|
|
long getDataSourceSize() {
|
|
return this.dataSourceSize;
|
|
}
|
|
|
|
/**
|
|
* Sets the total size of the data source.
|
|
*
|
|
* @param dataSourceSize The data source size.
|
|
*/
|
|
void setDataSourceSize(long dataSourceSize) {
|
|
this.dataSourceSize = dataSourceSize;
|
|
}
|
|
|
|
/**
|
|
* Gets the node data as a byte array that can be sent to the coordination
|
|
* service.
|
|
*
|
|
* @return The node data as a byte array.
|
|
*/
|
|
byte[] toArray() {
|
|
ByteBuffer buffer = ByteBuffer.allocate(MAX_POSSIBLE_NODE_DATA_SIZE);
|
|
|
|
// Write data (compatible with version 0)
|
|
buffer.putInt(this.processingStatus);
|
|
buffer.putInt(this.priority);
|
|
buffer.putInt(this.numberOfCrashes);
|
|
buffer.putLong(this.completedDate);
|
|
buffer.putInt(this.errorsOccurred ? 1 : 0);
|
|
|
|
if (this.version >= 1) {
|
|
// Write version
|
|
buffer.putInt(this.version);
|
|
|
|
// Write data
|
|
putStringIntoBuffer(deviceId, buffer, TypeKind.BYTE);
|
|
putStringIntoBuffer(caseName, buffer, TypeKind.BYTE);
|
|
putStringIntoBuffer(caseDirectoryPath, buffer, TypeKind.SHORT);
|
|
buffer.putLong(this.manifestFileDate);
|
|
putStringIntoBuffer(manifestFilePath, buffer, TypeKind.SHORT);
|
|
putStringIntoBuffer(dataSourcePath, buffer, TypeKind.SHORT);
|
|
buffer.put(this.processingStage);
|
|
buffer.putLong(this.processingStageStartDate);
|
|
putStringIntoBuffer(this.processingStageDetailsDescription, buffer, TypeKind.BYTE);
|
|
buffer.putLong(this.processingStageDetailsStartDate);
|
|
putStringIntoBuffer(processingHostName, buffer, TypeKind.SHORT);
|
|
|
|
if (this.version >= 2) {
|
|
buffer.putLong(this.dataSourceSize);
|
|
}
|
|
}
|
|
|
|
// Prepare the array
|
|
byte[] array = new byte[buffer.position()];
|
|
buffer.rewind();
|
|
buffer.get(array, 0, array.length);
|
|
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* This method retrieves a string from a given buffer. Depending on the type
|
|
* specified, either a 'byte' or a 'short' will first be read out of the
|
|
* buffer which gives the length of the string so it can be properly parsed.
|
|
*
|
|
* @param buffer The buffer from which the string will be read.
|
|
* @param lengthType The size of the length data.
|
|
*
|
|
* @return The string read from the buffer.
|
|
*/
|
|
private String getStringFromBuffer(ByteBuffer buffer, TypeKind lengthType) {
|
|
int length = 0;
|
|
String output = "";
|
|
|
|
switch (lengthType) {
|
|
case BYTE:
|
|
length = buffer.get();
|
|
break;
|
|
case SHORT:
|
|
length = buffer.getShort();
|
|
break;
|
|
}
|
|
|
|
if (length > 0) {
|
|
byte[] array = new byte[length];
|
|
buffer.get(array, 0, length);
|
|
output = new String(array);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* This method puts a given string into a given buffer. Depending on the
|
|
* type specified, either a 'byte' or a 'short' will be inserted prior to
|
|
* the string which gives the length of the string so it can be properly
|
|
* parsed.
|
|
*
|
|
* @param stringValue The string to write to the buffer.
|
|
* @param buffer The buffer to which the string will be written.
|
|
* @param lengthType The size of the length data.
|
|
*/
|
|
private void putStringIntoBuffer(String stringValue, ByteBuffer buffer, TypeKind lengthType) {
|
|
switch (lengthType) {
|
|
case BYTE:
|
|
buffer.put((byte) stringValue.length());
|
|
break;
|
|
case SHORT:
|
|
buffer.putShort((short) stringValue.length());
|
|
break;
|
|
}
|
|
|
|
buffer.put(stringValue.getBytes());
|
|
}
|
|
|
|
final static class InvalidDataException extends Exception {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private InvalidDataException(String message) {
|
|
super(message);
|
|
}
|
|
|
|
private InvalidDataException(String message, Throwable cause) {
|
|
super(message, cause);
|
|
}
|
|
}
|
|
|
|
}
|