From 4aba8a0c7a5d92e7c2e27e4f1c430f91cfc1e207 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 30 Jan 2021 17:18:56 +0000 Subject: [PATCH] Layers: Lock cache file, bomb out after 30 seconds --- volatility3/framework/constants/__init__.py | 3 ++ volatility3/framework/layers/resources.py | 48 +++++++++++++-------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index c04fbdb46..d3cf87aa8 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -76,6 +76,9 @@ BUG_URL = "https://github.com/volatilityfoundation/volatility3/issues" ProgressCallback = Optional[Callable[[float, str], None]] """Type information for ProgressCallback objects""" +URLOPEN_LOCK_TIMEOUT = 30000 +"""Number of miliseconds to wait before timing-out on file locks""" + class Parallelism(enum.IntEnum): """An enumeration listing the different types of parallelism applied to diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index 63cfd9570..7bbbf7c0e 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -10,6 +10,7 @@ import logging import lzma import os import ssl +import time import urllib.parse import urllib.request import zipfile @@ -121,26 +122,39 @@ class ResourceAccessor(object): constants.CACHE_PATH, "data_" + hashlib.sha512(bytes(url, 'raw_unicode_escape')).hexdigest() + ".cache") - if not os.path.exists(temp_filename): - vollog.debug("Caching file at: {}".format(temp_filename)) + with open(temp_filename + '.lock', 'w'): + if not os.path.exists(temp_filename): + vollog.debug("Caching file at: {}".format(temp_filename)) - try: - content_length = fp.info().get('Content-Length', -1) - except AttributeError: - # If our fp doesn't have an info member, carry on gracefully - content_length = -1 - cache_file = open(temp_filename, "wb") + try: + content_length = fp.info().get('Content-Length', -1) + except AttributeError: + # If our fp doesn't have an info member, carry on gracefully + content_length = -1 + cache_file = open(temp_filename, "wb") - count = 0 - block = fp.read(block_size) - while block: - count += len(block) - if self._progress_callback: - self._progress_callback(count * 100 / max(count, int(content_length)), - "Reading file {}".format(url)) - cache_file.write(block) + count = 0 block = fp.read(block_size) - cache_file.close() + while block: + count += len(block) + if self._progress_callback: + self._progress_callback(count * 100 / max(count, int(content_length)), + "Reading file {}".format(url)) + cache_file.write(block) + block = fp.read(block_size) + cache_file.close() + else: + count = 0 + while os.path.exists(temp_filename + '.lock'): + count += 1 + if self._progress_callback: + self._progress_callback(0, "Waiting on lock to retrieve {}".format(url)) + time.sleep(0.1) + if count >= constants.URLOPEN_LOCK_TIMEOUT // 1000: + break + if not os.path.exists(temp_filename): + raise error.URLError("Unable to lock the cache for {}".format(url)) + # Re-open the cache with a different mode # Since we don't want people thinking they're able to save to the cache file, # open it in read mode only and allow breakages to happen if they wanted to write