Merge branch 'new_table_load' of github.com:sleuthkit/autopsy into 8146b-specialTreeWithPreparedStatements

This commit is contained in:
Greg DiCristofaro 2021-11-04 09:53:35 -04:00
commit 100db86ebc
8 changed files with 1036 additions and 124 deletions

View File

@ -46,8 +46,48 @@ FileExtRootFilter_documents_displayName=Documents
FileExtRootFilter_executable_displayName=Executable
FileExtRootFilter_image_displayName=Images
FileExtRootFilter_video_displayName=Video
FileSystemColumnUtils.abstractFileColumns.accessTimeColLbl=Access Time
FileSystemColumnUtils.abstractFileColumns.attrAddrColLbl=Attr. Addr.
FileSystemColumnUtils.abstractFileColumns.changeTimeColLbl=Change Time
FileSystemColumnUtils.abstractFileColumns.commentName=C
FileSystemColumnUtils.abstractFileColumns.countName=O
FileSystemColumnUtils.abstractFileColumns.createdTimeColLbl=Created Time
FileSystemColumnUtils.abstractFileColumns.extensionColLbl=Extension
FileSystemColumnUtils.abstractFileColumns.flagsDirColLbl=Flags(Dir)
FileSystemColumnUtils.abstractFileColumns.flagsMetaColLbl=Flags(Meta)
FileSystemColumnUtils.abstractFileColumns.groupidColLbl=GroupID
FileSystemColumnUtils.abstractFileColumns.knownColLbl=Known
FileSystemColumnUtils.abstractFileColumns.locationColLbl=Location
FileSystemColumnUtils.abstractFileColumns.md5HashColLbl=MD5 Hash
FileSystemColumnUtils.abstractFileColumns.metaAddrColLbl=Meta Addr.
FileSystemColumnUtils.abstractFileColumns.mimeType=MIME Type
FileSystemColumnUtils.abstractFileColumns.modeColLbl=Mode
FileSystemColumnUtils.abstractFileColumns.modifiedTimeColLbl=Modified Time
FileSystemColumnUtils.abstractFileColumns.objectId=Object ID
FileSystemColumnUtils.abstractFileColumns.originalName=Original Name
FileSystemColumnUtils.abstractFileColumns.scoreName=S
FileSystemColumnUtils.abstractFileColumns.sha256HashColLbl=SHA-256 Hash
FileSystemColumnUtils.abstractFileColumns.sizeColLbl=Size
FileSystemColumnUtils.abstractFileColumns.typeDirColLbl=Type(Dir)
FileSystemColumnUtils.abstractFileColumns.typeMetaColLbl=Type(Meta)
FileSystemColumnUtils.abstractFileColumns.useridColLbl=UserID
FileSystemColumnUtils.imageColumns.devID=Device ID
FileSystemColumnUtils.imageColumns.sectorSize=Sector Size (Bytes)
FileSystemColumnUtils.imageColumns.size=Size (Bytes)
FileSystemColumnUtils.imageColumns.timezone=Timezone
FileSystemColumnUtils.imageColumns.type=Type
FileSystemColumnUtils.imageColumns.typeValue=Image
FileSystemColumnUtils.nameColumn.name=Name
FileSystemColumnUtils.noDescription=No Description
FileSystemColumnUtils.poolColumns.type=Type
FileSystemColumnUtils.volumeColumns.desc=Description
FileSystemColumnUtils.volumeColumns.flags=Flags
FileSystemColumnUtils.volumeColumns.id=ID
FileSystemColumnUtils.volumeColumns.length=Length in Sectors
FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector
FileTag.name.text=File Tag
FileTypesByMimeType.name.text=By MIME Type
ResultTag.name.text=Result Tag
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
TagsDAO.fileColumns.changeTimeColLbl=Changed Time
TagsDAO.fileColumns.commentColLbl=Comment
@ -66,30 +106,3 @@ TagsDAO.tagColumns.sourceNameColLbl=Source Name
TagsDAO.tagColumns.sourcePathColLbl=Source File Path
TagsDAO.tagColumns.typeColLbl=Result Type
TagsDAO.tagColumns.userNameColLbl=User Name
ThreePanelViewsDAO.fileColumns.accessTimeColLbl=Access Time
ThreePanelViewsDAO.fileColumns.attrAddrColLbl=Attr. Addr.
ThreePanelViewsDAO.fileColumns.changeTimeColLbl=Change Time
ThreePanelViewsDAO.fileColumns.commentName=C
ThreePanelViewsDAO.fileColumns.countName=O
ThreePanelViewsDAO.fileColumns.createdTimeColLbl=Created Time
ThreePanelViewsDAO.fileColumns.extensionColLbl=Extension
ThreePanelViewsDAO.fileColumns.flagsDirColLbl=Flags(Dir)
ThreePanelViewsDAO.fileColumns.flagsMetaColLbl=Flags(Meta)
ThreePanelViewsDAO.fileColumns.groupidColLbl=GroupID
ThreePanelViewsDAO.fileColumns.knownColLbl=Known
ThreePanelViewsDAO.fileColumns.locationColLbl=Location
ThreePanelViewsDAO.fileColumns.md5HashColLbl=MD5 Hash
ThreePanelViewsDAO.fileColumns.metaAddrColLbl=Meta Addr.
ThreePanelViewsDAO.fileColumns.mimeType=MIME Type
ThreePanelViewsDAO.fileColumns.modeColLbl=Mode
ThreePanelViewsDAO.fileColumns.modifiedTimeColLbl=Modified Time
ThreePanelViewsDAO.fileColumns.nameColLbl=Name
ThreePanelViewsDAO.fileColumns.noDescription=No Description
ThreePanelViewsDAO.fileColumns.objectId=Object ID
ThreePanelViewsDAO.fileColumns.originalName=Original Name
ThreePanelViewsDAO.fileColumns.scoreName=S
ThreePanelViewsDAO.fileColumns.sha256HashColLbl=SHA-256 Hash
ThreePanelViewsDAO.fileColumns.sizeColLbl=Size
ThreePanelViewsDAO.fileColumns.typeDirColLbl=Type(Dir)
ThreePanelViewsDAO.fileColumns.typeMetaColLbl=Type(Meta)
ThreePanelViewsDAO.fileColumns.useridColLbl=UserID

View File

@ -0,0 +1,535 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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 org.sleuthkit.autopsy.mainui.datamodel;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.datamodel.VolumeSystem;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utility class for creating consistent table data.
*/
class FileSystemColumnUtils {
private static final Logger logger = Logger.getLogger(FileSystemColumnUtils.class.getName());
enum ContentType {
IMAGE,
POOL,
VOLUME,
ABSTRACT_FILE,
UNSUPPORTED;
}
@Messages({"FileSystemColumnUtils.nameColumn.name=Name",
"FileSystemColumnUtils.abstractFileColumns.originalName=Original Name",
"FileSystemColumnUtils.abstractFileColumns.scoreName=S",
"FileSystemColumnUtils.abstractFileColumns.commentName=C",
"FileSystemColumnUtils.abstractFileColumns.countName=O",
"FileSystemColumnUtils.abstractFileColumns.locationColLbl=Location",
"FileSystemColumnUtils.abstractFileColumns.modifiedTimeColLbl=Modified Time",
"FileSystemColumnUtils.abstractFileColumns.changeTimeColLbl=Change Time",
"FileSystemColumnUtils.abstractFileColumns.accessTimeColLbl=Access Time",
"FileSystemColumnUtils.abstractFileColumns.createdTimeColLbl=Created Time",
"FileSystemColumnUtils.abstractFileColumns.sizeColLbl=Size",
"FileSystemColumnUtils.abstractFileColumns.flagsDirColLbl=Flags(Dir)",
"FileSystemColumnUtils.abstractFileColumns.flagsMetaColLbl=Flags(Meta)",
"FileSystemColumnUtils.abstractFileColumns.modeColLbl=Mode",
"FileSystemColumnUtils.abstractFileColumns.useridColLbl=UserID",
"FileSystemColumnUtils.abstractFileColumns.groupidColLbl=GroupID",
"FileSystemColumnUtils.abstractFileColumns.metaAddrColLbl=Meta Addr.",
"FileSystemColumnUtils.abstractFileColumns.attrAddrColLbl=Attr. Addr.",
"FileSystemColumnUtils.abstractFileColumns.typeDirColLbl=Type(Dir)",
"FileSystemColumnUtils.abstractFileColumns.typeMetaColLbl=Type(Meta)",
"FileSystemColumnUtils.abstractFileColumns.knownColLbl=Known",
"FileSystemColumnUtils.abstractFileColumns.md5HashColLbl=MD5 Hash",
"FileSystemColumnUtils.abstractFileColumns.sha256HashColLbl=SHA-256 Hash",
"FileSystemColumnUtils.abstractFileColumns.objectId=Object ID",
"FileSystemColumnUtils.abstractFileColumns.mimeType=MIME Type",
"FileSystemColumnUtils.abstractFileColumns.extensionColLbl=Extension",
"FileSystemColumnUtils.volumeColumns.id=ID",
"FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector",
"FileSystemColumnUtils.volumeColumns.length=Length in Sectors",
"FileSystemColumnUtils.volumeColumns.desc=Description",
"FileSystemColumnUtils.volumeColumns.flags=Flags",
"FileSystemColumnUtils.imageColumns.type=Type",
"FileSystemColumnUtils.imageColumns.typeValue=Image",
"FileSystemColumnUtils.imageColumns.size=Size (Bytes)",
"FileSystemColumnUtils.imageColumns.sectorSize=Sector Size (Bytes)",
"FileSystemColumnUtils.imageColumns.timezone=Timezone",
"FileSystemColumnUtils.imageColumns.devID=Device ID",
"FileSystemColumnUtils.poolColumns.type=Type",
"FileSystemColumnUtils.noDescription=No Description"})
private static final ColumnKey NAME_COLUMN = getColumnKey(Bundle.FileSystemColumnUtils_nameColumn_name());
private static final List<ColumnKey> ABSTRACT_FILE_COLUMNS = Arrays.asList(
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_originalName()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_scoreName()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_commentName()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_countName()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_locationColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_modifiedTimeColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_changeTimeColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_accessTimeColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_createdTimeColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_sizeColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_flagsDirColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_flagsMetaColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_modeColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_useridColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_groupidColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_metaAddrColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_attrAddrColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_typeDirColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_typeMetaColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_knownColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_md5HashColLbl()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_sha256HashColLbl()),
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_objectId()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_mimeType()),
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_extensionColLbl()));
private static final List<ColumnKey> VOLUME_COLUMNS = Arrays.asList(
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_id()),
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_startingSector()),
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_length()),
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_desc()),
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_flags()));
private static final List<ColumnKey> IMAGE_COLUMNS = Arrays.asList(
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_type()),
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_size()),
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_sectorSize()),
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_timezone()),
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_devID())
);
// Not used yet - Note that Hosts aren't content and will not be combined with other types, so we include the name here
private static final List<ColumnKey> HOST_COLUMNS = Arrays.asList(
NAME_COLUMN
);
private static final List<ColumnKey> POOL_COLUMNS = Arrays.asList(
getColumnKey(Bundle.FileSystemColumnUtils_poolColumns_type())
);
/**
* Convert a given Content object to an enum.
*
* @param content The Content object.
*
* @return The type corresponding to the content; UNSUPPORTED if the content will not be displayed
*/
private static ContentType getContentType(Content content) {
if (content instanceof Image) {
return ContentType.IMAGE;
} else if (content instanceof Volume) {
return ContentType.VOLUME;
} else if (content instanceof Pool) {
return ContentType.POOL;
} else if (content instanceof AbstractFile) {
return ContentType.ABSTRACT_FILE;
}
return ContentType.UNSUPPORTED;
}
/**
* Check whether a given content object should be displayed.
* We can display an object if ContentType is not UNSUPPORTED
* and if it is not the root directory.
*
* @param content The content.
*
* @return True if the content is displayable, false otherwise.
*/
static boolean isDisplayable(Content content) {
if (content instanceof AbstractFile) {
return ! ((AbstractFile)content).isRoot();
}
return (getContentType(content) != ContentType.UNSUPPORTED);
}
/**
* Get a list of the content types from the list that will be displayed.
* Call this before getColumnKeysForContent() and getCellValuesForContent()
* to ensure consistent columns.
*
* @param contentList List of content.
*
* @return List of types that will be displayed.
*/
static List<ContentType> getDisplayableTypesForContentList(List<Content> contentList) {
List<ContentType> displayableTypes = new ArrayList<>();
for (Content content : contentList) {
ContentType type = getContentType(content);
if (type != ContentType.UNSUPPORTED && ! displayableTypes.contains(type)) {
displayableTypes.add(type);
}
}
Collections.sort(displayableTypes);
return displayableTypes;
}
/**
* Get the column keys corresponding to the given list of types.
*
* @param contentTypes The list of types.
*
* @return The list of column keys.
*/
static List<ColumnKey> getColumnKeysForContent(List<ContentType> contentTypes) {
List<ColumnKey> colKeys = new ArrayList<>();
colKeys.add(NAME_COLUMN);
// Make sure content types are processed in the same order as in getCellValuesForContent()
if (contentTypes.contains(ContentType.IMAGE)) {
colKeys.addAll(IMAGE_COLUMNS);
}
if (contentTypes.contains(ContentType.POOL)) {
colKeys.addAll(POOL_COLUMNS);
}
if (contentTypes.contains(ContentType.VOLUME)) {
colKeys.addAll(VOLUME_COLUMNS);
}
if (contentTypes.contains(ContentType.ABSTRACT_FILE)) {
colKeys.addAll(ABSTRACT_FILE_COLUMNS);
}
return colKeys;
}
/**
* Get the cell values for a given content object.
*
* @param content The content to display.
* @param contentTypes The content types being displayed in the table.
*
* @return The cell values for this row.
*
* @throws TskCoreException
*/
static List<Object> getCellValuesForContent(Content content, List<ContentType> contentTypes) throws TskCoreException {
List<Object> cellValues = new ArrayList<>();
cellValues.add(getNameValueForContent(content));
// Make sure content types are processed in the same order as in getColumnKeysForContent()
if (contentTypes.contains(ContentType.IMAGE)) {
cellValues.addAll(getNonNameCellValuesForImage(content));
}
if (contentTypes.contains(ContentType.POOL)) {
cellValues.addAll(getNonNameCellValuesForPool(content));
}
if (contentTypes.contains(ContentType.VOLUME)) {
cellValues.addAll(getNonNameCellValuesForVolume(content));
}
if (contentTypes.contains(ContentType.ABSTRACT_FILE)) {
cellValues.addAll(getNonNameCellValuesForAbstractFile(content));
}
return cellValues;
}
/**
* Get the value for the name column for the given content.
*
* @param content The content.
*
* @return The display name for the content.
*/
private static String getNameValueForContent(Content content) {
if (content instanceof Image) {
Image image = (Image)content;
return image.getName();
} else if (content instanceof Volume) {
Volume vol = (Volume)content;
return getVolumeDisplayName(vol);
} else if (content instanceof Pool) {
Pool pool = (Pool)content;
return pool.getType().getName(); // We currently use the type name for both the name and type fields
} else if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile)content;
return file.getName(); // GVDTODO handle . and .. from getContentDisplayName()
}
return content.getName();
}
/**
* Get the column keys for an abstract file object.
* Only use this method if all rows contain AbstractFile objects.
* Make sure the order here matches that in getCellValuesForAbstractFile();
*
* @return The list of column keys.
*/
static List<ColumnKey> getColumnKeysForAbstractfile() {
List<ColumnKey> colKeys = new ArrayList<>();
colKeys.add(NAME_COLUMN);
colKeys.addAll(ABSTRACT_FILE_COLUMNS);
return colKeys;
}
/**
* Get the cell values for an abstract file.
* Only use this method if all rows contain AbstractFile objects.
* Make sure the order here matches that in getColumnKeysForAbstractfile();
*
* @param file The file to use to populate the cells.
*
* @return List of cell values.
*/
static List<Object> getCellValuesForAbstractFile(AbstractFile file) throws TskCoreException {
List<Object> cells = new ArrayList<>();
cells.add(getNameValueForContent(file));
cells.addAll(getNonNameCellValuesForAbstractFile(file));
return cells;
}
/**
* Make sure the order here matches that in ABSTRACT_FILE_COLUMNS
*
* @param content The content to use to populate the cells (may not be an abstract file)
*
* @return List of cell values
*/
private static List<Object> getNonNameCellValuesForAbstractFile(Content content) throws TskCoreException {
final int nColumns = 17;
if (! (content instanceof AbstractFile)) {
return Collections.nCopies(nColumns, null);
}
// Make sure to update nColumns if the number of columns here changes
AbstractFile file = (AbstractFile) content;
return Arrays.asList(
// GVDTODO translation column
null,
//GVDTDO replace nulls with SCO
null,
null,
null,
file.getUniquePath(),
TimeZoneUtils.getFormattedTime(file.getMtime()),
TimeZoneUtils.getFormattedTime(file.getCtime()),
TimeZoneUtils.getFormattedTime(file.getAtime()),
TimeZoneUtils.getFormattedTime(file.getCrtime()),
file.getSize(),
file.getDirFlagAsString(),
file.getMetaFlagsAsString(),
// mode,
// userid,
// groupid,
// metaAddr,
// attrAddr,
// typeDir,
// typeMeta,
file.getKnown().getName(),
StringUtils.defaultString(file.getMd5Hash()),
StringUtils.defaultString(file.getSha256Hash()),
// objectId,
StringUtils.defaultString(file.getMIMEType()),
file.getNameExtension()
);
}
/**
* Make sure the order here matches that in POOL_COLUMNS
*
* @param conent The content to use to populate the cells (may not be a pool)
*
* @return List of cell values
*/
private static List<Object> getNonNameCellValuesForPool(Content content) throws TskCoreException {
final int nColumns = 1;
if (! (content instanceof Pool)) {
return Collections.nCopies(nColumns, null);
}
// Make sure to update nColumns if the number of columns here changes
Pool pool = (Pool) content;
return Arrays.asList(
pool.getType().getName() // We currently use the type name for both the name and type fields
);
}
/**
* Make sure the order here matches that in HOST_COLUMNS
*
* @param host The host to use to populate the cells
*
* @return List of cell values
*/
static List<Object> getCellValuesForHost(Host host) throws TskCoreException {
return Arrays.asList(
host.getName()
);
}
/**
* Make sure the order here matches that in IMAGE_COLUMNS
*
* @param content The content to use to populate the cells (may not be an image)
*
* @return List of cell values
*/
private static List<Object> getNonNameCellValuesForImage(Content content) throws TskCoreException {
final int nColumns = 5;
if (! (content instanceof Image)) {
return Collections.nCopies(nColumns, null);
}
// Make sure to update nColumns if the number of columns here changes
Image image = (Image) content;
return Arrays.asList(
Bundle.FileSystemColumnUtils_imageColumns_typeValue(),
image.getSize(),
image.getSsize(),
image.getTimeZone(),
image.getDeviceId()
);
}
/**
* Make sure the order here matches that in VOLUME_COLUMNS
*
* @param content The content to use to populate the cells (may not be a volume)
*
* @return List of cell values
*/
private static List<Object> getNonNameCellValuesForVolume(Content content) throws TskCoreException {
final int nColumns = 5;
if (! (content instanceof Volume)) {
return Collections.nCopies(nColumns, null);
}
// Make sure to update nColumns if the number of columns here changes
Volume vol = (Volume) content;
return Arrays.asList(
vol.getAddr(),
vol.getStart(),
vol.getLength(),
vol.getDescription(),
vol.getFlagsAsString()
);
}
/**
* Get the display name for a volume.
*
* @param vol The volume.
*
* @return The display name.
*/
private static String getVolumeDisplayName(Volume vol) {
// set name, display name, and icon
String volName = "vol" + Long.toString(vol.getAddr());
long end = vol.getStart() + (vol.getLength() - 1);
String tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + "-" + end + ")";
// If this is a pool volume use a custom display name
try {
if (vol.getParent() != null
&& vol.getParent().getParent() instanceof Pool) {
// Pool volumes are not contiguous so printing a range of blocks is inaccurate
tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + ")";
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error looking up parent(s) of volume with obj ID = " + vol.getId(), ex);
}
return tempVolName;
}
/**
* Get the content that should be displayed in the table based on the given object.
* Algorithm:
* - If content is already displayable, return it
* - If content is a volume system, return its displayable children
* - If content is a file system, return the displayable children of the root folder
* - If content is the root folder, return the displayable children of the root folder
*
* @param content The base content.
*
* @return List of content to add to the table.
*/
static List<Content> getNextDisplayableContent(Content content) throws TskCoreException {
// If the given content is displayable, return it
if (FileSystemColumnUtils.isDisplayable(content)) {
return Arrays.asList(content);
}
List<Content> contentToDisplay = new ArrayList<>();
if (content instanceof VolumeSystem) {
// Return all children that can be displayed
VolumeSystem vs = (VolumeSystem)content;
for (Content child : vs.getChildren()) {
if (isDisplayable(child)) {
contentToDisplay.add(child);
}
}
} else if (content instanceof FileSystem) {
// Return the children of the root node
FileSystem fs = (FileSystem)content;
for (Content child : fs.getRootDirectory().getChildren()) {
if (isDisplayable(child)) {
contentToDisplay.add(child);
}
}
} else if (content instanceof AbstractFile) {
if (((AbstractFile) content).isRoot()) {
// If we have the root folder, skip it and display the children
for (Content child : content.getChildren()) {
if (isDisplayable(child)) {
contentToDisplay.add(child);
}
}
} else {
return Arrays.asList(content);
}
}
return contentToDisplay;
}
/**
* Create a column key from a string.
*
* @param name The column name
*
* @return The column key
*/
private static ColumnKey getColumnKey(String name) {
return new ColumnKey(name, name, Bundle.FileSystemColumnUtils_noDescription());
}
}

View File

@ -0,0 +1,61 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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 org.sleuthkit.autopsy.mainui.datamodel;
import java.util.Objects;
/**
* Key for content object in order to retrieve data from DAO.
*/
public class FileSystemContentSearchParam {
private final Long contentObjectId;
public FileSystemContentSearchParam(Long contentObjectId) {
this.contentObjectId = contentObjectId;
}
public Long getContentObjectId() {
return contentObjectId;
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.contentObjectId);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FileSystemContentSearchParam other = (FileSystemContentSearchParam) obj;
if (!Objects.equals(this.contentObjectId, other.contentObjectId)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,152 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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 org.sleuthkit.autopsy.mainui.datamodel;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*/
public class FileSystemDAO {
private static final int CACHE_SIZE = 15; // rule of thumb: 5 entries times number of cached SearchParams sub-types
private static final long CACHE_DURATION = 2;
private static final TimeUnit CACHE_DURATION_UNITS = TimeUnit.MINUTES;
private final Cache<SearchParams<?>, BaseSearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build();
private static final String FILE_SYSTEM_TYPE_ID = "FILE_SYSTEM";
private static FileSystemDAO instance = null;
synchronized static FileSystemDAO getInstance() {
if (instance == null) {
instance = new FileSystemDAO();
}
return instance;
}
private BaseSearchResultsDTO fetchContentForTableFromContent(SearchParams<FileSystemContentSearchParam> cacheKey) throws NoCurrentCaseException, TskCoreException {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Long objectId = cacheKey.getParamData().getContentObjectId();
List<Content> contentForTable = new ArrayList<>();
String parentName = "";
Content parentContent = skCase.getContentById(objectId);
if (parentContent == null) {
throw new TskCoreException("Error loading children of object with ID " + objectId);
}
parentName = parentContent.getName();
for (Content content : parentContent.getChildren()) {
contentForTable.addAll(FileSystemColumnUtils.getNextDisplayableContent(content));
}
return fetchContentForTable(cacheKey, contentForTable, parentName);
}
private BaseSearchResultsDTO fetchContentForTableFromHost(SearchParams<FileSystemHostSearchParam> cacheKey) throws NoCurrentCaseException, TskCoreException {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Long objectId = cacheKey.getParamData().getHostObjectId();
List<Content> contentForTable = new ArrayList<>();
String parentName = "";
Optional<Host> host = skCase.getHostManager().getHostById(objectId);
if (host.isPresent()) {
parentName = host.get().getName();
contentForTable.addAll(skCase.getHostManager().getDataSourcesForHost(host.get()));
} else {
throw new TskCoreException("Error loading host with ID " + objectId);
}
return fetchContentForTable(cacheKey, contentForTable, parentName);
}
private BaseSearchResultsDTO fetchContentForTable(SearchParams<?> cacheKey, List<Content> contentForTable,
String parentName) throws NoCurrentCaseException, TskCoreException {
// Ensure consistent columns for each page by doing this before paging
List<FileSystemColumnUtils.ContentType> displayableTypes = FileSystemColumnUtils.getDisplayableTypesForContentList(contentForTable);
List<Content> pagedContent = getPaged(contentForTable, cacheKey);
List<ColumnKey> columnKeys = FileSystemColumnUtils.getColumnKeysForContent(displayableTypes);
List<RowDTO> rows = new ArrayList<>();
for (Content content : pagedContent) {
List<Object> cellValues = FileSystemColumnUtils.getCellValuesForContent(content, displayableTypes);
rows.add(new BaseRowDTO(cellValues, FILE_SYSTEM_TYPE_ID, content.getId()));
}
return new BaseSearchResultsDTO(FILE_SYSTEM_TYPE_ID, parentName, columnKeys, rows, cacheKey.getStartItem(), contentForTable.size());
}
/**
* Returns a list of paged content.
*
* @param contentObjects The content objects.
* @param searchParams The search parameters including the paging.
*
* @return The list of paged artifacts.
*/
private List<Content> getPaged(List<? extends Content> contentObjects, SearchParams<?> searchParams) {
Stream<? extends Content> pagedArtsStream = contentObjects.stream()
.sorted(Comparator.comparing((conent) -> conent.getId()))
.skip(searchParams.getStartItem());
if (searchParams.getMaxResultsCount() != null) {
pagedArtsStream = pagedArtsStream.limit(searchParams.getMaxResultsCount());
}
return pagedArtsStream.collect(Collectors.toList());
}
public BaseSearchResultsDTO getContentForTable(FileSystemContentSearchParam objectKey, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
SearchParams<FileSystemContentSearchParam> searchParams = new SearchParams<>(objectKey, startItem, maxCount);
if (hardRefresh) {
searchParamsCache.invalidate(searchParams);
}
return searchParamsCache.get(searchParams, () -> fetchContentForTableFromContent(searchParams));
}
public BaseSearchResultsDTO getContentForTable(FileSystemHostSearchParam objectKey, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
SearchParams<FileSystemHostSearchParam> searchParams = new SearchParams<>(objectKey, startItem, maxCount);
if (hardRefresh) {
searchParamsCache.invalidate(searchParams);
}
return searchParamsCache.get(searchParams, () -> fetchContentForTableFromHost(searchParams));
}
}

View File

@ -0,0 +1,62 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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 org.sleuthkit.autopsy.mainui.datamodel;
import java.util.Objects;
/**
* Key for content object in order to retrieve data from DAO.
*/
public class FileSystemHostSearchParam {
private final Long hostObjectId;
public FileSystemHostSearchParam(Long hostObjectId) {
this.hostObjectId = hostObjectId;
}
public Long getHostObjectId() {
return hostObjectId;
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.hostObjectId);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FileSystemHostSearchParam other = (FileSystemHostSearchParam) obj;
if (!Objects.equals(this.hostObjectId, other.hostObjectId)) {
return false;
}
return true;
}
}

View File

@ -37,6 +37,7 @@ public class MainDAO {
private final DataArtifactDAO dataArtifactDAO = DataArtifactDAO.getInstance();
private final AnalysisResultDAO analysisResultDAO = AnalysisResultDAO.getInstance();
private final ViewsDAO viewsDAO = ViewsDAO.getInstance();
private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance();
private final TagsDAO tagsDAO = TagsDAO.getInstance();
public DataArtifactDAO getDataArtifactsDAO() {
@ -51,7 +52,11 @@ public class MainDAO {
return viewsDAO;
}
public FileSystemDAO getFileSystemDAO() {
return fileSystemDAO;
}
public TagsDAO getTagsDAO() {
return tagsDAO;
}
}
}

View File

@ -50,33 +50,6 @@ import org.sleuthkit.datamodel.TskData;
* Provides information to populate the results viewer for data in the views
* section.
*/
@Messages({"ThreePanelViewsDAO.fileColumns.nameColLbl=Name",
"ThreePanelViewsDAO.fileColumns.originalName=Original Name",
"ThreePanelViewsDAO.fileColumns.scoreName=S",
"ThreePanelViewsDAO.fileColumns.commentName=C",
"ThreePanelViewsDAO.fileColumns.countName=O",
"ThreePanelViewsDAO.fileColumns.locationColLbl=Location",
"ThreePanelViewsDAO.fileColumns.modifiedTimeColLbl=Modified Time",
"ThreePanelViewsDAO.fileColumns.changeTimeColLbl=Change Time",
"ThreePanelViewsDAO.fileColumns.accessTimeColLbl=Access Time",
"ThreePanelViewsDAO.fileColumns.createdTimeColLbl=Created Time",
"ThreePanelViewsDAO.fileColumns.sizeColLbl=Size",
"ThreePanelViewsDAO.fileColumns.flagsDirColLbl=Flags(Dir)",
"ThreePanelViewsDAO.fileColumns.flagsMetaColLbl=Flags(Meta)",
"ThreePanelViewsDAO.fileColumns.modeColLbl=Mode",
"ThreePanelViewsDAO.fileColumns.useridColLbl=UserID",
"ThreePanelViewsDAO.fileColumns.groupidColLbl=GroupID",
"ThreePanelViewsDAO.fileColumns.metaAddrColLbl=Meta Addr.",
"ThreePanelViewsDAO.fileColumns.attrAddrColLbl=Attr. Addr.",
"ThreePanelViewsDAO.fileColumns.typeDirColLbl=Type(Dir)",
"ThreePanelViewsDAO.fileColumns.typeMetaColLbl=Type(Meta)",
"ThreePanelViewsDAO.fileColumns.knownColLbl=Known",
"ThreePanelViewsDAO.fileColumns.md5HashColLbl=MD5 Hash",
"ThreePanelViewsDAO.fileColumns.sha256HashColLbl=SHA-256 Hash",
"ThreePanelViewsDAO.fileColumns.objectId=Object ID",
"ThreePanelViewsDAO.fileColumns.mimeType=MIME Type",
"ThreePanelViewsDAO.fileColumns.extensionColLbl=Extension",
"ThreePanelViewsDAO.fileColumns.noDescription=No Description"})
public class ViewsDAO {
private static final int CACHE_SIZE = 15; // rule of thumb: 5 entries times number of cached SearchParams sub-types
@ -86,34 +59,6 @@ public class ViewsDAO {
private static final String FILE_VIEW_EXT_TYPE_ID = "FILE_VIEW_BY_EXT";
private static final List<ColumnKey> FILE_COLUMNS = Arrays.asList(
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_nameColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_originalName()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_scoreName()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_commentName()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_countName()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_locationColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_modifiedTimeColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_changeTimeColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_accessTimeColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_createdTimeColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_sizeColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_flagsDirColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_flagsMetaColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_modeColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_useridColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_groupidColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_metaAddrColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_attrAddrColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_typeDirColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_typeMetaColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_knownColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_md5HashColLbl()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_sha256HashColLbl()),
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_objectId()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_mimeType()),
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_extensionColLbl()));
private static ViewsDAO instance = null;
synchronized static ViewsDAO getInstance() {
@ -124,10 +69,6 @@ public class ViewsDAO {
return instance;
}
private static ColumnKey getFileColumnKey(String name) {
return new ColumnKey(name, name, Bundle.ThreePanelViewsDAO_fileColumns_noDescription());
}
static ExtensionMediaType getExtensionMediaType(String ext) {
if (StringUtils.isBlank(ext)) {
return ExtensionMediaType.UNCATEGORIZED;
@ -374,38 +315,7 @@ public class ViewsDAO {
List<RowDTO> fileRows = new ArrayList<>();
for (AbstractFile file : files) {
List<Object> cellValues = Arrays.asList(
file.getName(), // GVDTODO handle . and .. from getContentDisplayName()
// GVDTODO translation column
null,
//GVDTDO replace nulls with SCO
null,
null,
null,
file.getUniquePath(),
TimeZoneUtils.getFormattedTime(file.getMtime()),
TimeZoneUtils.getFormattedTime(file.getCtime()),
TimeZoneUtils.getFormattedTime(file.getAtime()),
TimeZoneUtils.getFormattedTime(file.getCrtime()),
file.getSize(),
file.getDirFlagAsString(),
file.getMetaFlagsAsString(),
// mode,
// userid,
// groupid,
// metaAddr,
// attrAddr,
// typeDir,
// typeMeta,
file.getKnown().getName(),
StringUtils.defaultString(file.getMd5Hash()),
StringUtils.defaultString(file.getSha256Hash()),
// objectId,
StringUtils.defaultString(file.getMIMEType()),
file.getNameExtension()
);
List<Object> cellValues = FileSystemColumnUtils.getCellValuesForAbstractFile(file);
fileRows.add(new FileRowDTO(
file,
@ -418,7 +328,7 @@ public class ViewsDAO {
cellValues));
}
return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, displayName, FILE_COLUMNS, fileRows, startItem, totalResultsCount);
return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, displayName, FileSystemColumnUtils.getColumnKeysForAbstractfile(), fileRows, startItem, totalResultsCount);
}
/**

View File

@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.testutils.CaseUtils;
import org.sleuthkit.autopsy.testutils.TestUtilsException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.Attribute;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -44,11 +45,19 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.datamodel.VolumeSystem;
/**
*
@ -139,6 +148,19 @@ public class TableSearchTest extends NbTestCase {
AnalysisResult keywordHitAnalysisResult = null; // A keyword hit
Content keywordHitSource = null; // The source of the keyword hit above
// File system test
Host fsTestHostA = null; // A host
Image fsTestImageA = null; // An image
VolumeSystem fsTestVsA = null; // A volume system
Volume fsTestVolumeA1 = null; // A volume
Volume fsTestVolumeA2 = null; // Another volume
Volume fsTestVolumeA3 = null; // Another volume
FileSystem fsTestFsA = null; // A file system
AbstractFile fsTestRootDirA = null; // The root directory
Image fsTestImageB = null; // Another image
Volume fsTestVolumeB1 = null; // Another volume
Pool fsTestPoolB = null; // A pool
// Tags test
TagName knownTag1 = null;
TagName tag2 = null;
@ -167,6 +189,7 @@ public class TableSearchTest extends NbTestCase {
mimeSearchTest();
extensionSearchTest();
sizeSearchTest();
fileSystemTest();
tagsTest();
}
@ -174,6 +197,7 @@ public class TableSearchTest extends NbTestCase {
* Create a case and add sample data.
*/
private void setUpCaseDatabase() {
SleuthkitCase.CaseDbTransaction trans = null;
try {
// Create a test case
openCase = CaseUtils.createAsCurrentCase("testTableSearchCase");
@ -182,11 +206,12 @@ public class TableSearchTest extends NbTestCase {
tagsManager = openCase.getServices().getTagsManager();
// Add two logical files data sources
SleuthkitCase.CaseDbTransaction trans = db.beginTransaction();
trans = db.beginTransaction();
dataSource1 = db.addLocalFilesDataSource("devId1", "C:\\Fake\\Path\\1", "EST", null, trans);
dataSource2 = db.addLocalFilesDataSource("devId2", "C:\\Fake\\Path\\2", "EST", null, trans);
dataSource3 = db.addLocalFilesDataSource("devId3", "C:\\Fake\\Path\\3", "EST", null, trans);
trans.commit();
trans = null;
// Add files
AbstractFile folderA1 = db.addLocalDirectory(dataSource1.getId(), "folder1");
@ -341,6 +366,76 @@ public class TableSearchTest extends NbTestCase {
null, KEYWORD_SET_1, null, attrs).getAnalysisResult();
keywordHitSource = hashHitAnalysisResult;
// Create a normal image
// fsTestImageA (Host: fsTestHostA)
// - fsTestVsA
// -- fsTestVolumeA1
// --- fsTestFsA
// ---- fsTestRootDirA
// ----- (3 files)
// -- fsTestVolumeA2
// -- fsTestVolumeA3
fsTestHostA = db.getHostManager().newHost("File system test host");
trans = db.beginTransaction();
fsTestImageA = db.addImage(TskData.TSK_IMG_TYPE_ENUM.TSK_IMG_TYPE_DETECT, 512, 1024, "image1", Arrays.asList("C:\\Fake\\Path\\4"),
"EST", null, null, null, "deviceID12345", fsTestHostA, trans);
fsTestVsA = db.addVolumeSystem(fsTestImageA.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_DOS, 0, 1024, trans);
fsTestVolumeA1 = db.addVolume(fsTestVsA.getId(), 0, 0, 512, "Test vol A1", 0, trans);
fsTestVolumeA2 = db.addVolume(fsTestVsA.getId(), 1, 512, 512, "Test vol A2", 0, trans);
fsTestVolumeA3 = db.addVolume(fsTestVsA.getId(), 2, 1024, 512, "Test vol A3", 0, trans);
long rootInum = 1;
fsTestFsA = db.addFileSystem(fsTestVolumeA1.getId(), 0, TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_EXT2, 512, 1,
rootInum, rootInum, 10, "Test file system", trans);
trans.commit();
trans = null;
fsTestRootDirA = db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
"", rootInum, 0,
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 0,
0, 0, 0, 0, false, fsTestFsA);
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
"Test file 1", 0, 0,
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 123,
0, 0, 0, 0, true, fsTestRootDirA);
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
"Test file 2", 0, 0,
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 456,
0, 0, 0, 0, true, fsTestRootDirA);
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
"Test file 3", 0, 0,
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 789,
0, 0, 0, 0, true, fsTestRootDirA);
// Create an image with some odd structures for testing
trans = db.beginTransaction();
fsTestImageB = db.addImage(TskData.TSK_IMG_TYPE_ENUM.TSK_IMG_TYPE_DETECT, 512, 1024, "image2", Arrays.asList("C:\\Fake\\Path\\5"),
"EST", null, null, null, "deviceID678", fsTestHostA, trans);
// Images can have VS, pool, FS, file, artifact, and report children.
// Add a VS, pool, and local file
VolumeSystem vsB = db.addVolumeSystem(fsTestImageB.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_BSD, 0, 2048, trans);
db.addPool(fsTestImageB.getId(), TskData.TSK_POOL_TYPE_ENUM.TSK_POOL_TYPE_APFS, trans);
db.addLocalFile("Test local file B1", "C:\\Fake\\Path\\6", 6000, 0, 0, 0, 0,
true, TskData.EncodingType.NONE, fsTestImageB, trans);
// Volumes can have pool, FS, file, and artifact children
fsTestVolumeB1 = db.addVolume(vsB.getId(), 0, 0, 512, "Test vol B1", 0, trans);
fsTestPoolB = db.addPool(fsTestVolumeB1.getId(), TskData.TSK_POOL_TYPE_ENUM.TSK_POOL_TYPE_APFS, trans);
db.addLocalFile("Test local file B2", "C:\\Fake\\Path\\7", 7000, 0, 0, 0, 0,
true, TskData.EncodingType.NONE, fsTestVolumeB1, trans);
// Pools can have VS, file, and artifact children
VolumeSystem vsB2 = db.addVolumeSystem(fsTestPoolB.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_GPT, 0, 2048, trans);
db.addVolume(vsB2.getId(), 0, 0, 512, "Test vol B2", 0, trans);
db.addLocalFile("Test local file B3", "C:\\Fake\\Path\\8", 8000, 0, 0, 0, 0,
true, TskData.EncodingType.NONE, fsTestPoolB, trans);
trans.commit();
trans = null;
// Add tags ----
knownTag1 = tagsManager.addTagName("Tag 1", TAG_DESCRIPTION, TagName.HTML_COLOR.RED, TskData.FileKnown.KNOWN);
tag2 = tagsManager.addTagName("Tag 2", "Descrition");
@ -357,9 +452,16 @@ public class TableSearchTest extends NbTestCase {
openCase.getServices().getTagsManager().addContentTag(fileB1, tag2);
// Tag the custom file in data source 2
openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1);
openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1);
} catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException ex) {
if (trans != null) {
try {
trans.rollback();
} catch (TskCoreException ex2) {
Exceptions.printStackTrace(ex2);
}
}
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
@ -815,7 +917,7 @@ public class TableSearchTest extends NbTestCase {
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
}
}
public void extensionSearchTest() {
// Quick test that everything is initialized
@ -877,6 +979,78 @@ public class TableSearchTest extends NbTestCase {
Assert.fail(ex.getMessage());
}
}
private void fileSystemTest() {
// Quick test that everything is initialized
assertTrue(db != null);
try {
FileSystemDAO fileSystemDAO = MainDAO.getInstance().getFileSystemDAO();
// HostA is associated with two images
FileSystemHostSearchParam hostParam = new FileSystemHostSearchParam(fsTestHostA.getHostId());
BaseSearchResultsDTO results = fileSystemDAO.getContentForTable(hostParam, 0, null, false);
assertEquals(2, results.getTotalResultsCount());
assertEquals(2, results.getItems().size());
// ImageA has one volume system child, which has three volumes that will be displayed
FileSystemContentSearchParam param = new FileSystemContentSearchParam(fsTestImageA.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// VsA has three volume children (this should match the previous search)
param = new FileSystemContentSearchParam(fsTestVsA.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// VolumeA1 has a file system child, which in turn has a root directory child with three file children
param = new FileSystemContentSearchParam(fsTestVolumeA1.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// FsA has a root directory child with three file children (this should match the previous search)
param = new FileSystemContentSearchParam(fsTestFsA.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// The root dir contains three files
param = new FileSystemContentSearchParam(fsTestRootDirA.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// ImageB has VS (which will display one volume), pool, and one local file children
param = new FileSystemContentSearchParam(fsTestImageB.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(3, results.getTotalResultsCount());
assertEquals(3, results.getItems().size());
// Check that we have the "Type" column from the Pool and the "Known" column from the file
List<String> columnDisplayNames = results.getColumns().stream().map(p -> p.getDisplayName()).collect(Collectors.toList());
assertTrue(columnDisplayNames.contains("Type"));
assertTrue(columnDisplayNames.contains("Known"));
// fsTestVolumeB1 has pool and one local file children
param = new FileSystemContentSearchParam(fsTestVolumeB1.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(2, results.getTotalResultsCount());
assertEquals(2, results.getItems().size());
// fsTestPoolB has VS (which will display one volume) and local file children
param = new FileSystemContentSearchParam(fsTestPoolB.getId());
results = fileSystemDAO.getContentForTable(param, 0, null, false);
assertEquals(2, results.getTotalResultsCount());
assertEquals(2, results.getItems().size());
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage());
}
}
private enum CustomRootFilter implements FileExtSearchFilter {