mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge pull request #2616 from millmanorama/2404-cached-file-race-condition
2404 fix race condition when accessing cached thumbnail files
This commit is contained in:
commit
c066935745
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-16 Basis Technology Corp.
|
||||
* Copyright 2011-17 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* Contact: aebadirad <at> 42six <dot> com
|
||||
@ -40,6 +40,7 @@ import java.util.List;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -93,7 +94,16 @@ public class ImageUtils {
|
||||
private static final List<String> SUPPORTED_IMAGE_EXTENSIONS = new ArrayList<>();
|
||||
private static final SortedSet<String> SUPPORTED_IMAGE_MIME_TYPES;
|
||||
|
||||
private static final boolean openCVLoaded;
|
||||
private static final boolean OPEN_CV_LOADED;
|
||||
|
||||
/**
|
||||
* Map from tsk object id to Java File object. Used to get the same File for
|
||||
* different tasks related to the same object so we can then synchronize on
|
||||
* the File.
|
||||
*
|
||||
* NOTE: Must be cleared when the case is changed.
|
||||
*/
|
||||
private static final ConcurrentHashMap<Long, File> cacheFileMap = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
ImageIO.scanForPlugins();
|
||||
@ -124,7 +134,7 @@ public class ImageUtils {
|
||||
|
||||
}
|
||||
|
||||
openCVLoaded = openCVLoadedTemp;
|
||||
OPEN_CV_LOADED = openCVLoadedTemp;
|
||||
SUPPORTED_IMAGE_EXTENSIONS.addAll(Arrays.asList(ImageIO.getReaderFileSuffixes()));
|
||||
SUPPORTED_IMAGE_EXTENSIONS.add("tec"); // Add JFIF .tec files
|
||||
SUPPORTED_IMAGE_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
|
||||
@ -139,6 +149,9 @@ public class ImageUtils {
|
||||
"image/x-portable-bitmap", //NON-NLS
|
||||
"application/x-123")); //TODO: is this correct? -jm //NON-NLS
|
||||
SUPPORTED_IMAGE_MIME_TYPES.removeIf("application/octet-stream"::equals); //NON-NLS
|
||||
|
||||
//Clear the file map when the case changes, so we don't accidentaly get images from the old case.
|
||||
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), evt -> cacheFileMap.clear());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,7 +160,7 @@ public class ImageUtils {
|
||||
private static FileTypeDetector fileTypeDetector;
|
||||
|
||||
/**
|
||||
* thread that saves generated thumbnails to disk in the background
|
||||
*Thread/Executor that saves generated thumbnails to disk in the background
|
||||
*/
|
||||
private static final Executor imageSaver
|
||||
= Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
|
||||
@ -296,7 +309,7 @@ public class ImageUtils {
|
||||
try {
|
||||
return SwingFXUtils.fromFXImage(thumbnailTask.get(), null);
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get thumbnail for {0}: " + ex.toString(), getContentPathSafe(content)); //NON-NLS
|
||||
LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), ex); //NON-NLS
|
||||
return DEFAULT_THUMBNAIL;
|
||||
}
|
||||
} else {
|
||||
@ -305,7 +318,6 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get a thumbnail of a specified size for the given image. Generates the
|
||||
* thumbnail if it is not already cached.
|
||||
*
|
||||
@ -332,13 +344,15 @@ public class ImageUtils {
|
||||
* any problem getting the file, such as no case was open.
|
||||
*/
|
||||
private static File getCachedThumbnailLocation(long fileID) {
|
||||
try {
|
||||
String cacheDirectory = Case.getCurrentCase().getCacheDirectory();
|
||||
return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS
|
||||
} catch (IllegalStateException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
return cacheFileMap.computeIfAbsent(fileID, id -> {
|
||||
try {
|
||||
String cacheDirectory = Case.getCurrentCase().getCacheDirectory();
|
||||
return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS
|
||||
} catch (IllegalStateException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -489,6 +503,7 @@ public class ImageUtils {
|
||||
"ImageIO could not determine height of {0}: ", //NON-NLS
|
||||
imageReader -> imageReader.getHeight(0)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -576,6 +591,7 @@ public class ImageUtils {
|
||||
*/
|
||||
public static Task<javafx.scene.image.Image> newGetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
|
||||
return new GetThumbnailTask(file, iconSize, defaultOnFailure);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -609,16 +625,21 @@ public class ImageUtils {
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If a thumbnail file is already saved locally, just read that.
|
||||
if (cacheFile != null && cacheFile.exists()) {
|
||||
try {
|
||||
BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
|
||||
if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
|
||||
return SwingFXUtils.toFXImage(cachedThumbnail, null);
|
||||
if (cacheFile != null) {
|
||||
synchronized (cacheFile) {
|
||||
if (cacheFile.exists()) {
|
||||
try {
|
||||
BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
|
||||
if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
|
||||
return SwingFXUtils.toFXImage(cachedThumbnail, null);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOGGER.log(Level.WARNING, "ImageIO had a problem reading the cached thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
|
||||
cacheFile.delete(); //since we can't read the file we might as well delete it.
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOGGER.log(Level.WARNING, "ImageIO had a problem reading the cached thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
|
||||
cacheFile.delete(); //since we can't read the file we might as well delete it.
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,7 +650,7 @@ public class ImageUtils {
|
||||
//There was no correctly-sized cached thumbnail so make one.
|
||||
BufferedImage thumbnail = null;
|
||||
if (VideoUtils.isVideoThumbnailSupported(file)) {
|
||||
if (openCVLoaded) {
|
||||
if (OPEN_CV_LOADED) {
|
||||
updateMessage(Bundle.GetOrGenerateThumbnailTask_generatingPreviewFor(file.getName()));
|
||||
thumbnail = VideoUtils.generateVideoThumbnail(file, iconSize);
|
||||
}
|
||||
@ -698,12 +719,14 @@ public class ImageUtils {
|
||||
private void saveThumbnail(BufferedImage thumbnail) {
|
||||
imageSaver.execute(() -> {
|
||||
try {
|
||||
Files.createParentDirs(cacheFile);
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete();
|
||||
synchronized (cacheFile) {
|
||||
Files.createParentDirs(cacheFile);
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
ImageIO.write(thumbnail, FORMAT, cacheFile);
|
||||
}
|
||||
ImageIO.write(thumbnail, FORMAT, cacheFile);
|
||||
} catch (IllegalArgumentException | IOException ex) {
|
||||
} catch (Exception ex) {
|
||||
LOGGER.log(Level.WARNING, "Could not write thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
|
||||
}
|
||||
});
|
||||
@ -725,6 +748,7 @@ public class ImageUtils {
|
||||
*/
|
||||
public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
|
||||
return new ReadImageTask(file);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -753,7 +777,6 @@ public class ImageUtils {
|
||||
|
||||
private static final String IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT = "ImageIO could not read {0}. It may be unsupported or corrupt"; //NON-NLS
|
||||
final AbstractFile file;
|
||||
// private ImageReader reader;
|
||||
|
||||
ReadImageTaskBase(AbstractFile file) {
|
||||
this.file = file;
|
||||
|
Loading…
x
Reference in New Issue
Block a user