Skip to content

Commit 604fc95

Browse files
committed
Moves checksum to top level of file cache
1 parent aab4142 commit 604fc95

File tree

2 files changed

+73
-75
lines changed

2 files changed

+73
-75
lines changed

lib/msf/core/modules/metadata/store.rb

Lines changed: 63 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def initialize
1616

1717
BaseMetaDataFile = 'modules_metadata_base.json'
1818
UserMetaDataFile = 'modules_metadata.json'
19-
CacheMetaDataFile = 'cache_metadata_base.json'
19+
CacheMetaDataFile = 'module_metadata_cache.json'
2020

2121
#
2222
# Initializes from user store (under ~/store/.msf4) if it exists. else base file (under $INSTALL_ROOT/db) is copied and loaded.
@@ -138,31 +138,33 @@ def self.valid_checksum?
138138

139139
# If no cached checksum exists, create the cache file with current checksum
140140
if cached_sha.nil?
141-
create_or_update_cache_file(get_cache_path, current_checksum)
141+
update_cache_checksum(current_checksum)
142142
return false
143143
end
144144

145145
checksums_match?(current_checksum, cached_sha)
146146
end
147147

148148
# Calculate the current checksum for all module and library files
149-
# This calculates checksums for each file, caches them, and then
150-
# generates an overall checksum from the individual file checksums.
149+
# This calculates checksums for each file and generates an overall checksum
150+
# from the individual file checksums. Does NOT update the cached checksum.
151151
#
152-
# @return [String] The current overall checksum
152+
# @return [Integer] The current overall checksum
153153
def self.get_current_checksum
154154
files = collect_files_to_check
155-
per_file_cache_file = get_per_file_cache_path
156-
per_file_cache = load_per_file_cache(per_file_cache_file)
155+
cache_file = get_cache_path
156+
cache_data = load_combined_cache(cache_file)
157157

158-
file_crc32s_with_metadata = calculate_file_checksums(files, per_file_cache)
158+
files_lookup = {}
159+
cache_data['files'].each { |entry| files_lookup[entry['path']] = entry }
159160

160-
updated_cache = file_crc32s_with_metadata.to_h
161-
file_crc32s = file_crc32s_with_metadata.map { |_, meta| meta['crc32'] }
161+
file_crc32s_with_metadata = calculate_file_checksums(files, files_lookup)
162162

163-
save_per_file_cache(per_file_cache_file, updated_cache)
163+
file_crc32s = file_crc32s_with_metadata.map { |_, meta| meta['crc32'] }.sort
164164

165-
calculate_overall_checksum(file_crc32s)
165+
overall_checksum = calculate_overall_checksum(file_crc32s)
166+
167+
overall_checksum
166168
end
167169

168170
# Compare the current checksum with the cached checksum
@@ -175,9 +177,9 @@ def self.checksums_match?(current_checksum, cached_checksum)
175177

176178
# Calculate the overall checksum from individual file checksums
177179
# @param [Array<Integer>] file_crc32s Array of individual file CRC32 values
178-
# @return [String] The hexadecimal representation of the overall CRC32
180+
# @return [Integer] The overall CRC32 as an integer
179181
def self.calculate_overall_checksum(file_crc32s)
180-
Zlib.crc32(file_crc32s.join).to_s(16)
182+
Zlib.crc32(file_crc32s.join(','), 0)
181183
end
182184

183185
# Collect all files that need to be checked for checksums
@@ -204,7 +206,7 @@ def self.calculate_file_checksums(files, cache)
204206
if cache_entry && cache_entry['mtime'] == file_metadata.mtime.to_i && cache_entry['size'] == file_metadata.size
205207
crc32 = cache_entry['crc32']
206208
else
207-
crc32 = File.open(file, 'rb') { |fd| Zlib.crc32(fd.read) }
209+
crc32 = File.open(file, 'rb') { |fd| Zlib.crc32(fd.read, 0) }
208210
end
209211
# Return file and its metadata for later aggregation
210212
[file, {
@@ -215,80 +217,74 @@ def self.calculate_file_checksums(files, cache)
215217
end
216218
end
217219

218-
# Get the path to the per-file cache
219-
# @return [String] Path to the per-file cache
220-
def self.get_per_file_cache_path
221-
File.join(Msf::Config.config_directory, 'store', 'per_file_metadata_cache.json')
222-
end
223-
224220
# Get the path to the cache file
225221
# @return [String] Path to the cache file
226222
def self.get_cache_path
227223
File.join(Msf::Config.config_directory, "store", CacheMetaDataFile)
228224
end
229225

230-
# Load the per-file cache from disk
226+
# Load the combined cache from disk (contains both files and checksum)
231227
# @param [String] cache_file Path to the cache file
232-
# @return [Hash] The loaded cache or an empty hash if the file doesn't exist
233-
def self.load_per_file_cache(cache_file)
234-
File.exist?(cache_file) ? JSON.parse(File.read(cache_file)) : {}
228+
# @return [Hash] The loaded cache with 'files' and 'checksum' keys, or empty structure if file doesn't exist
229+
def self.load_combined_cache(cache_file)
230+
if File.exist?(cache_file)
231+
cache_content = JSON.parse(File.read(cache_file))
232+
# Ensure the cache has the expected structure
233+
{
234+
'files' => cache_content['files'] || [],
235+
'checksum' => cache_content['checksum']
236+
}
237+
else
238+
{ 'files' => [], 'checksum' => nil }
239+
end
235240
end
236241

237-
# Save the updated per-file cache to disk
242+
# Save the combined cache to disk (files and checksum in one file)
238243
# @param [String] cache_file Path to the cache file
239-
# @param [Hash] updated_cache The cache data to save
244+
# @param [Hash] files_cache The per-file cache data
245+
# @param [Integer] overall_checksum The overall checksum
240246
# @return [void]
241-
def self.save_per_file_cache(cache_file, updated_cache)
247+
def self.save_combined_cache(cache_file, files_cache, overall_checksum)
242248
# Ensure the directory for the cache file exists before writing
243249
FileUtils.mkdir_p(File.dirname(cache_file))
244-
# Save the updated per-file cache to disk
245-
File.write(cache_file, JSON.pretty_generate(updated_cache))
246-
end
247250

248-
# Create or update a cache file with the given checksum
249-
# @param [String] file_path Path to the cache file
250-
# @param [String] checksum The checksum to store
251-
# @return [void]
252-
def self.create_or_update_cache_file(file_path, checksum)
253-
# Ensure directory exists
254-
FileUtils.mkdir_p(File.dirname(file_path))
255-
256-
if File.exist?(file_path)
257-
# Update existing file
258-
cache_content = JSON.parse(File.read(file_path))
259-
cache_content['checksum']['crc32'] = checksum
260-
else
261-
# Create new file
262-
cache_content = {
263-
"checksum" => {
264-
"crc32" => checksum
265-
}
266-
}
267-
end
251+
cache_content = {
252+
'checksum' => overall_checksum,
253+
'files' => files_cache
254+
}
268255

269-
File.write(file_path, JSON.pretty_generate(cache_content))
256+
File.write(cache_file, JSON.pretty_generate(cache_content))
270257
end
271258

272-
# Get the cached checksum value from the user's cache file
273-
# @return [String, nil] The cached checksum value or nil if no cache exists
259+
# Get the cached checksum value from the combined cache file
260+
# @return [Integer, nil] The cached checksum value or nil if no cache exists
274261
def self.get_cached_checksum
275262
cache_path = get_cache_path
276-
277-
if File.exist?(cache_path)
278-
cache_content = JSON.parse(File.read(cache_path))
279-
return cache_content.dig('checksum', 'crc32')
280-
end
281-
282-
# If no cache exists, return nil to trigger creation of a new cache file
283-
nil
263+
cache_data = load_combined_cache(cache_path)
264+
cache_data['checksum']
284265
end
285266

286-
# Update the cache checksum file with the current crc32 checksum of the module paths.
287-
#
288-
# @param [String] current_checksum The current checksum to store in the cache
267+
# Update the cache with the current checksum and file data
268+
# @param [Integer] current_checksum The current checksum to store in the cache
289269
# @return [void]
290270
def self.update_cache_checksum(current_checksum)
291-
cache_path = get_cache_path
292-
create_or_update_cache_file(cache_path, current_checksum)
271+
# Recalculate file checksums and update both overall checksum and file cache
272+
files = collect_files_to_check
273+
cache_file = get_cache_path
274+
cache_data = load_combined_cache(cache_file)
275+
276+
files_lookup = {}
277+
cache_data['files'].each { |entry| files_lookup[entry['path']] = entry }
278+
279+
file_crc32s_with_metadata = calculate_file_checksums(files, files_lookup)
280+
281+
updated_files_cache = file_crc32s_with_metadata.map do |file_path, metadata|
282+
metadata.merge('path' => file_path)
283+
end
284+
285+
updated_files_cache.sort_by! { |entry| entry['path'] }
286+
287+
# Save both the updated file cache and the new overall checksum
288+
save_combined_cache(cache_file, updated_files_cache, current_checksum)
293289
end
294290
end

lib/msf/ui/console/driver.rb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {
7272
elog(e)
7373
end
7474

75+
# Check if files have been modified and force immediate loading if so
76+
has_modified_metasploit_files = !Msf::Modules::Metadata::Store.valid_checksum?
77+
78+
if has_modified_metasploit_files
79+
current_checksum = Msf::Modules::Metadata::Store.get_current_checksum
80+
Msf::Modules::Metadata::Store.update_cache_checksum(current_checksum)
81+
# Force immediate module loading when files have changed
82+
opts['DeferModuleLoads'] = false
83+
end
84+
7585
if opts['DeferModuleLoads'].nil?
7686
opts['DeferModuleLoads'] = Msf::FeatureManager.instance.enabled?(Msf::FeatureManager::DEFER_MODULE_LOADS)
7787
end
@@ -163,14 +173,6 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {
163173
self.framework.init_module_paths(module_paths: opts['ModulePath'], defer_module_loads: opts['DeferModuleLoads'])
164174
end
165175

166-
has_modified_metasploit_files = !Msf::Modules::Metadata::Store.valid_checksum?
167-
168-
# Update the cache checksum if files have been modified
169-
if has_modified_metasploit_files
170-
current_checksum = Msf::Modules::Metadata::Store.get_current_checksum
171-
Msf::Modules::Metadata::Store.update_cache_checksum(current_checksum)
172-
end
173-
174176
# Refresh module cache if modules are modified, or we're not deferring loads
175177
if has_modified_metasploit_files || !opts['DeferModuleLoads']
176178
framework.threads.spawn("ModuleCacheRebuild", true) do

0 commit comments

Comments
 (0)