ProjectSource

Fingerprint::Checker

class Fingerprint::Checker

Given two fingerprints (master and copy) ensures that the copy has at least everything contained in master: For every file in the master, a corresponding file must exist in the copy. This means that there may be extraneous files in the copy, but ensures that every file in the master has been replicated accurately.

At this time, this implementation may require a large amount of memory, proportional to the number of files being checked.

Master and copy are +IO+ objects corresponding to the output produced by +Fingerprint::Scanner+.

Definitions

def check(&block)

Run the checking process.

Implementation

def check(&block)
	# For every file in the src, we check that it exists
	# in the destination:
	total_count = @master.records.count
	processed_size = 0
	total_size = @master.records.inject(0) { |count, record| count + (record['file.size'] || 0).to_i }
	
	if @options[:additions]
		copy_paths = @copy.paths.dup
	else
		copy_paths = {}
	end
	
	@master.records.each_with_index do |record, processed_count|
		copy_paths.delete(record.path)

		if @options[:progress]
			$stderr.puts "# Checking: #{record.path}"
		end

		next if record.mode != :file

		result, message = @copy.compare(record)
		if result != :valid
			yield record, result, message
		elsif @options[:extended]
			# Extended check compares other attributes such as user, group, file modes.
			changes = record.diff(copy.paths[record.path])
			
			if changes.size > 0
				yield record, :attribute_changed, "Attribute(s) #{changes.join(', ')} changed"
			end
		end
		
		if @options[:progress]
			$stderr.puts "# Progress: File #{processed_count} / #{total_count}; Byte #{processed_size} / #{total_size} = #{sprintf('%0.2f%%', processed_size.to_f / total_size.to_f * 100.0)}"

			processed_size += (record['file.size'] || 0).to_i
		end
	end
	
	if @options[:additions]
		copy_paths.each do |path, record|
			next unless record.mode == :file || record.mode == :directory
			
			yield record, :addition, "File added"
		end
	end
end

attr :failures

A list of files which either did not exist in the copy, or had the wrong checksum.

def self.verify(master, copy, **options, &block)

Helper function to check two fingerprint files.

Implementation

def self.verify(master, copy, **options, &block)
	error_count = 0 

	errors = options.delete(:recordset) || RecordSet.new
	if options[:output]
		errors = RecordSetPrinter.new(errors, options[:output])
	end

	checker = Checker.new(master, copy, **options)

	checker.check do |record, result, message|
		error_count += 1

		metadata = {
			'error.code' => result,
			'error.message' => message
		}

		if result == :addition
			metadata.merge!(record.metadata)
			
			errors << Record.new(:warning, record.path, metadata)
		elsif (copy = checker.copy.paths[record.path])
			changes = record.diff(copy)

			changes.each do |name|
				metadata["changes.#{name}.old"] = record[name]
				metadata["changes.#{name}.new"] = copy[name]
			end

			errors << Record.new(:warning, record.path, metadata)
		else
			errors << Record.new(:warning, record.path, metadata)
		end
	end

	if error_count
		summary_message = "#{error_count} error(s) detected."
	else
		summary_message = "No errors detected"
	end

	errors << Record.new(:summary, summary_message, {
		'error.count' => error_count
	})

	return error_count
end