debian-mirror-gitlab/app/services/pages/zip_directory_service.rb

98 lines
3 KiB
Ruby
Raw Normal View History

2021-02-22 17:27:13 +05:30
# frozen_string_literal: true
module Pages
class ZipDirectoryService
2021-03-08 18:12:59 +05:30
include BaseServiceUtility
include Gitlab::Utils::StrongMemoize
# used only to track exceptions in Sentry
InvalidEntryError = Class.new(StandardError)
2021-02-22 17:27:13 +05:30
PUBLIC_DIR = 'public'
2021-03-11 19:13:27 +05:30
attr_reader :public_dir, :real_dir
def initialize(input_dir, ignore_invalid_entries: false)
2021-03-08 18:12:59 +05:30
@input_dir = input_dir
2021-03-11 19:13:27 +05:30
@ignore_invalid_entries = ignore_invalid_entries
2021-02-22 17:27:13 +05:30
end
def execute
2021-04-29 21:17:54 +05:30
return success unless resolve_public_dir
2021-03-08 18:12:59 +05:30
output_file = File.join(real_dir, "@migrated.zip") # '@' to avoid any name collision with groups or projects
FileUtils.rm_f(output_file)
2021-02-22 17:27:13 +05:30
2021-03-08 18:12:59 +05:30
entries_count = 0
2022-01-26 12:08:38 +05:30
# Since we're writing not reading here, we can safely silence the cop.
# It currently cannot discern between opening for reading or writing.
::Zip::File.open(output_file, ::Zip::File::CREATE) do |zipfile| # rubocop:disable Performance/Rubyzip
2021-02-22 17:27:13 +05:30
write_entry(zipfile, PUBLIC_DIR)
2021-03-08 18:12:59 +05:30
entries_count = zipfile.entries.count
2021-02-22 17:27:13 +05:30
end
2021-03-08 18:12:59 +05:30
success(archive_path: output_file, entries_count: entries_count)
2021-06-08 01:23:25 +05:30
rescue StandardError => e
2021-03-08 18:12:59 +05:30
FileUtils.rm_f(output_file) if output_file
raise e
2021-02-22 17:27:13 +05:30
end
private
2021-03-11 19:13:27 +05:30
def resolve_public_dir
@real_dir = File.realpath(@input_dir)
@public_dir = File.join(real_dir, PUBLIC_DIR)
valid_path?(public_dir)
rescue Errno::ENOENT
false
end
2021-02-22 17:27:13 +05:30
def write_entry(zipfile, zipfile_path)
2021-03-08 18:12:59 +05:30
disk_file_path = File.join(real_dir, zipfile_path)
2021-02-22 17:27:13 +05:30
unless valid_path?(disk_file_path)
# archive with invalid entry will just have this entry missing
2021-03-11 19:13:27 +05:30
raise InvalidEntryError, "#{disk_file_path} is invalid, input_dir: #{@input_dir}"
2021-02-22 17:27:13 +05:30
end
2021-03-11 19:13:27 +05:30
ftype = File.lstat(disk_file_path).ftype
case ftype
2021-02-22 17:27:13 +05:30
when 'directory'
recursively_zip_directory(zipfile, disk_file_path, zipfile_path)
when 'file', 'link'
zipfile.add(zipfile_path, disk_file_path)
else
2021-03-11 19:13:27 +05:30
raise InvalidEntryError, "#{disk_file_path} has invalid ftype: #{ftype}, input_dir: #{@input_dir}"
2021-02-22 17:27:13 +05:30
end
2021-03-11 19:13:27 +05:30
rescue Errno::ENOENT, Errno::ELOOP, InvalidEntryError => e
2021-02-22 17:27:13 +05:30
Gitlab::ErrorTracking.track_exception(e, input_dir: @input_dir, disk_file_path: disk_file_path)
2021-03-11 19:13:27 +05:30
raise e unless @ignore_invalid_entries
2021-02-22 17:27:13 +05:30
end
def recursively_zip_directory(zipfile, disk_file_path, zipfile_path)
zipfile.mkdir(zipfile_path)
entries = Dir.entries(disk_file_path) - %w[. ..]
entries = entries.map { |entry| File.join(zipfile_path, entry) }
write_entries(zipfile, entries)
end
def write_entries(zipfile, entries)
entries.each do |zipfile_path|
write_entry(zipfile, zipfile_path)
end
end
2021-03-11 19:13:27 +05:30
# SafeZip was introduced only recently,
# so we have invalid entries on disk
2021-02-22 17:27:13 +05:30
def valid_path?(disk_file_path)
realpath = File.realpath(disk_file_path)
2021-03-08 18:12:59 +05:30
realpath == public_dir || realpath.start_with?(public_dir + "/")
end
2021-02-22 17:27:13 +05:30
end
end