fixes for deleted content

This commit is contained in:
Greg DiCristofaro 2021-12-09 14:30:29 -05:00
parent c556d26f55
commit aceac88f06
3 changed files with 83 additions and 31 deletions

View File

@ -37,6 +37,13 @@ public class DeletedContentSearchParams {
private final DeletedContentFilter filter;
private final Long dataSourceId;
/**
* Main constructor.
*
* @param filter The filter (if null, indicates full refresh
* required).
* @param dataSourceId The data source id or null.
*/
public DeletedContentSearchParams(DeletedContentFilter filter, Long dataSourceId) {
this.filter = filter;
this.dataSourceId = dataSourceId;

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.mainui.datamodel;
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import java.beans.PropertyChangeEvent;
import java.sql.SQLException;
import java.text.MessageFormat;
@ -37,7 +38,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@ -63,6 +63,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbPreparedStatement;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskData.FileKnown;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM;
@ -200,8 +201,9 @@ public class ViewsDAO extends AbstractDAO {
}
DeletedContentEvent deletedContentEvt = (DeletedContentEvent) eventData;
return deletedContentEvt.getFilter().equals(params.getFilter())
&& (params.getDataSourceId() == null || Objects.equals(params.getDataSourceId(), deletedContentEvt.getDataSourceId()));
return (deletedContentEvt.getFilter() == null || deletedContentEvt.getFilter().equals(params.getFilter()))
&& (params.getDataSourceId() == null || deletedContentEvt.getDataSourceId() == null
|| Objects.equals(params.getDataSourceId(), deletedContentEvt.getDataSourceId()));
}
/**
@ -546,6 +548,21 @@ public class ViewsDAO extends AbstractDAO {
* @throws ExecutionException
*/
public TreeResultsDTO<DeletedContentSearchParams> getDeletedContentCounts(Long dataSourceId) throws IllegalArgumentException, ExecutionException {
Set<DeletedContentFilter> indeterminateFilters = new HashSet<>();
for (DAOEvent evt : this.treeCounts.getEnqueued()) {
if (evt instanceof DeletedContentEvent) {
DeletedContentEvent deletedEvt = (DeletedContentEvent) evt;
if (dataSourceId == null || deletedEvt.getDataSourceId() == null || Objects.equals(deletedEvt.getDataSourceId(), dataSourceId)) {
if (deletedEvt.getFilter() == null) {
// if null filter, indicates full refresh and all file sizes need refresh.
indeterminateFilters.addAll(Arrays.asList(DeletedContentFilter.values()));
break;
} else {
indeterminateFilters.add(deletedEvt.getFilter());
}
}
}
}
String queryStr = Stream.of(DeletedContentFilter.values())
.map((filter) -> {
@ -563,7 +580,11 @@ public class ViewsDAO extends AbstractDAO {
if (resultSet.next()) {
for (DeletedContentFilter filter : DeletedContentFilter.values()) {
long count = resultSet.getLong(filter.name());
treeList.add(createDeletedContentTreeItem(filter, dataSourceId, TreeDisplayCount.getDeterminate(count)));
TreeDisplayCount displayCount = indeterminateFilters.contains(filter)
? TreeDisplayCount.INDETERMINATE
: TreeDisplayCount.getDeterminate(count);
treeList.add(createDeletedContentTreeItem(filter, dataSourceId, displayCount));
}
}
} catch (SQLException ex) {
@ -582,7 +603,7 @@ public class ViewsDAO extends AbstractDAO {
"DELETED_CONTENT",
new DeletedContentSearchParams(filter, dataSourceId),
filter,
filter.getDisplayName(),
filter == null ? "" : filter.getDisplayName(),
displayCount);
}
@ -930,7 +951,7 @@ public class ViewsDAO extends AbstractDAO {
@Override
Set<? extends DAOEvent> handleIngestComplete() {
SubDAOUtils.invalidateKeys(this.searchParamsCache,
(searchParams) -> searchParamsMatchEvent(null, null, null, null, true, searchParams));
(searchParams) -> searchParamsMatchEvent(null, null, null, null, null, true, searchParams));
Set<? extends DAOEvent> treeEvts = SubDAOUtils.getIngestCompleteEvents(this.treeCounts,
(daoEvt, count) -> createTreeItem(daoEvt, count));
@ -957,6 +978,7 @@ public class ViewsDAO extends AbstractDAO {
Long dsId = null;
boolean dataSourceAdded = false;
Set<FileExtSearchFilter> evtExtFilters = null;
Set<DeletedContentFilter> deletedContentFilters = null;
String evtMimeType = null;
FileSizeFilter evtFileSize = null;
@ -978,7 +1000,7 @@ public class ViewsDAO extends AbstractDAO {
evtExtFilters = EXTENSION_FILTER_MAP.getOrDefault("." + af.getNameExtension(), Collections.emptySet());
}
Set<DeletedContentFilter> deletedContentFilters = getMatchingDeletedContentFilters(af);
deletedContentFilters = getMatchingDeletedContentFilters(af);
// create a mime type mapping if mime type present
if (StringUtils.isBlank(af.getMIMEType()) || !TSK_FS_NAME_TYPE_ENUM.REG.equals(af.getDirType()) || !getMimeDbFilesTypes().contains(af.getType())) {
@ -993,26 +1015,29 @@ public class ViewsDAO extends AbstractDAO {
.orElse(null);
}
if (evtExtFilters == null || evtExtFilters.isEmpty() && deletedContentFilters.isEmpty() && evtMimeType == null && evtFileSize == null) {
return Collections.emptySet();
if (evtExtFilters == null || evtExtFilters.isEmpty() && deletedContentFilters.isEmpty() && evtMimeType == null && evtFileSize == null) {
return Collections.emptySet();
}
}
return invalidateAndReturnEvents(evtExtFilters, evtMimeType, evtFileSize, dsId, dataSourceAdded);
return invalidateAndReturnEvents(evtExtFilters, evtMimeType, evtFileSize, deletedContentFilters, dsId, dataSourceAdded);
}
/**
* Handles invalidating caches and returning events based on digest.
*
* @param evtExtFilters The file extension filters or empty set.
* @param evtMimeType The mime type or null.
* @param evtFileSize The file size filter or null.
* @param dsId The data source id or null.
* @param dataSourceAdded Whether or not this is a data source added event.
* @param evtExtFilters The file extension filters or empty set.
* @param evtMimeType The mime type or null.
* @param evtFileSize The file size filter or null.
* @param deletedContentFilters The set of affected deleted content filters.
* @param dsId The data source id or null.
* @param dataSourceAdded Whether or not this is a data source added
* event.
*
* @return The set of dao events to be fired.
*/
private Set<DAOEvent> invalidateAndReturnEvents(Set<FileExtSearchFilter> evtExtFilters, String evtMimeType,
FileSizeFilter evtFileSize, Long dsId, boolean dataSourceAdded) {
FileSizeFilter evtFileSize, Set<DeletedContentFilter> deletedContentFilters, Long dsId, boolean dataSourceAdded) {
SubDAOUtils.invalidateKeys(this.searchParamsCache,
(searchParams) -> searchParamsMatchEvent(evtExtFilters, deletedContentFilters,
@ -1049,8 +1074,8 @@ public class ViewsDAO extends AbstractDAO {
&& (sizeParams.getDataSourceId() == null || dsId == null || Objects.equals(sizeParams.getDataSourceId(), dsId));
} else if (searchParams instanceof DeletedContentSearchParams) {
DeletedContentSearchParams deletedParams = (DeletedContentSearchParams) searchParams;
return deletedContentFilters.contains(deletedParams.getFilter())
&& (deletedParams.getDataSourceId() == null || Objects.equals(deletedParams.getDataSourceId(), dsId));
return (dataSourceAdded || (deletedContentFilters != null && deletedContentFilters.contains(deletedParams.getFilter())))
&& (deletedParams.getDataSourceId() == null || dsId == null || Objects.equals(deletedParams.getDataSourceId(), dsId));
} else {
return false;
}
@ -1065,7 +1090,7 @@ public class ViewsDAO extends AbstractDAO {
* @param mimeType The affected mime type or null.
* @param sizeFilter The affected size filter or null.
* @param dsId The file object id.
* @param dataSourceAdded A data source was added.
* @param dataSourceAdded A data source was added.
*
* @return The list of affected dao events.
*/
@ -1073,17 +1098,18 @@ public class ViewsDAO extends AbstractDAO {
Set<DeletedContentFilter> deletedContentFilters,
String mimeType,
FileSizeFilter sizeFilter,
long dsId,
Long dsId,
boolean dataSourceAdded) {
List<DAOEvent> daoEvents = extFilters == null
? new ArrayList<>()
Stream<DAOEvent> extEvents = extFilters == null
? Stream.empty()
: extFilters.stream()
.map(extFilter -> new FileTypeExtensionsEvent(extFilter, dsId))
.collect(Collectors.toList());
.map(extFilter -> new FileTypeExtensionsEvent(extFilter, dsId));
Stream<DAOEvent> deletedEvents = deletedContentFilters.stream()
.map(deletedFilter -> new DeletedContentEvent(deletedFilter, dsId));
Stream<DAOEvent> deletedEvents = deletedContentFilters == null
? Stream.empty()
: deletedContentFilters.stream()
.map(deletedFilter -> new DeletedContentEvent(deletedFilter, dsId));
List<DAOEvent> daoEvents = Stream.concat(extEvents, deletedEvents)
.collect(Collectors.toList());
@ -1147,6 +1173,7 @@ public class ViewsDAO extends AbstractDAO {
*/
private Set<DAOEvent> getFileViewRefreshEvents(Long dataSourceId) {
return ImmutableSet.of(
new DeletedContentEvent(null, dataSourceId),
new FileTypeSizeEvent(null, dataSourceId),
new FileTypeExtensionsEvent(null, dataSourceId)
);

View File

@ -191,11 +191,29 @@ public class ViewsTypeFactory {
return MainDAO.getInstance().getViewsDAO().getDeletedContentCounts(dataSourceId);
}
@Override
protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) {
for (DAOEvent evt : aggEvt.getEvents()) {
if (evt instanceof TreeEvent) {
TreeResultsDTO.TreeItemDTO<DeletedContentSearchParams> treeItem = super.getTypedTreeItem((TreeEvent) evt, DeletedContentSearchParams.class);
// if search params has null filter, trigger full refresh
if (treeItem != null && treeItem.getSearchParams().getFilter() == null) {
super.update();
return;
}
}
}
super.handleDAOAggregateEvent(aggEvt);
}
@Override
protected TreeResultsDTO.TreeItemDTO<? extends DeletedContentSearchParams> getOrCreateRelevantChild(TreeEvent treeEvt) {
TreeResultsDTO.TreeItemDTO<DeletedContentSearchParams> originalTreeItem = super.getTypedTreeItem(treeEvt, DeletedContentSearchParams.class);
if (originalTreeItem != null
// only create child if size filter is present (if null, update should be triggered separately)
&& originalTreeItem.getSearchParams().getFilter() != null
&& (this.dataSourceId == null || Objects.equals(this.dataSourceId, originalTreeItem.getSearchParams().getDataSourceId()))) {
// generate new type so that if it is a subtree event (i.e. keyword hits), the right tree item is created.