90 lines
1.9 KiB
Ruby
90 lines
1.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'zlib'
|
|
|
|
module Gitlab
|
|
module ImportExport
|
|
class DecompressedArchiveSizeValidator
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
DEFAULT_MAX_BYTES = 10.gigabytes.freeze
|
|
CHUNK_SIZE = 4096.freeze
|
|
|
|
attr_reader :error
|
|
|
|
def initialize(archive_path:, max_bytes: self.class.max_bytes)
|
|
@archive_path = archive_path
|
|
@max_bytes = max_bytes
|
|
@bytes_read = 0
|
|
@total_reads = 0
|
|
@denominator = 5
|
|
@error = nil
|
|
end
|
|
|
|
def valid?
|
|
strong_memoize(:valid) do
|
|
validate
|
|
end
|
|
end
|
|
|
|
def self.max_bytes
|
|
DEFAULT_MAX_BYTES
|
|
end
|
|
|
|
def archive_file
|
|
@archive_file ||= File.open(@archive_path)
|
|
end
|
|
|
|
private
|
|
|
|
def validate
|
|
until archive_file.eof?
|
|
compressed_chunk = archive_file.read(CHUNK_SIZE)
|
|
|
|
inflate_stream.inflate(compressed_chunk) do |chunk|
|
|
@bytes_read += chunk.size
|
|
@total_reads += 1
|
|
end
|
|
|
|
# Start garbage collection every 5 reads in order
|
|
# to prevent memory bloat during archive decompression
|
|
GC.start if gc_start?
|
|
|
|
if @bytes_read > @max_bytes
|
|
@error = error_message
|
|
|
|
return false
|
|
end
|
|
end
|
|
|
|
true
|
|
rescue => e
|
|
@error = error_message
|
|
|
|
Gitlab::ErrorTracking.track_exception(e)
|
|
|
|
Gitlab::Import::Logger.info(
|
|
message: @error,
|
|
error: e.message
|
|
)
|
|
|
|
false
|
|
ensure
|
|
inflate_stream.close
|
|
archive_file.close
|
|
end
|
|
|
|
def inflate_stream
|
|
@inflate_stream ||= Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
|
end
|
|
|
|
def gc_start?
|
|
@total_reads % @denominator == 0
|
|
end
|
|
|
|
def error_message
|
|
_('Decompressed archive size validation failed.')
|
|
end
|
|
end
|
|
end
|
|
end
|