Class: FileDigests
- Inherits:
-
Object
- Object
- FileDigests
- Defined in:
- lib/file-digests.rb
Constant Summary collapse
- VERSION =
Gem.loaded_specs["file-digests"]&.version&.to_s
- DIGEST_ALGORITHMS =
["BLAKE2b512", "SHA3-256", "SHA512-256"]
- LEGACY_DIGEST_ALGORITHMS =
["SHA512", "SHA256"]
Class Method Summary collapse
- .canonical_digest_algorithm_name(string) ⇒ Object
- .digest_algorithms_list_text ⇒ Object
- .parse_cli_options ⇒ Object
- .run_cli_utility ⇒ Object
Instance Method Summary collapse
- #canonical_digest_algorithm_name(string) ⇒ Object
- #close_database ⇒ Object
-
#initialize(files_path, digest_database_path, options = {}) ⇒ FileDigests
constructor
A new instance of FileDigests.
- #perform_check ⇒ Object
- #show_duplicates ⇒ Object
Constructor Details
#initialize(files_path, digest_database_path, options = {}) ⇒ FileDigests
Returns a new instance of FileDigests.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/file-digests.rb', line 119 def initialize files_path, digest_database_path, = {} @options = @user_input_wait_time = 0 initialize_paths files_path, digest_database_path initialize_database @db.transaction(:exclusive) do if db_digest_algorithm = ("digest_algorithm") if @digest_algorithm = canonical_digest_algorithm_name(db_digest_algorithm) if @options[:digest_algorithm] && @options[:digest_algorithm] != @digest_algorithm @new_digest_algorithm = @options[:digest_algorithm] end else raise "Database contains data for unsupported digest algorithm: #{db_digest_algorithm}" end else @digest_algorithm = (@options[:digest_algorithm] || "BLAKE2b512") "digest_algorithm", @digest_algorithm end end puts "Using #{@digest_algorithm} digest algorithm" if @options[:verbose] end |
Class Method Details
.canonical_digest_algorithm_name(string) ⇒ Object
30 31 32 33 34 35 36 |
# File 'lib/file-digests.rb', line 30 def self.canonical_digest_algorithm_name(string) if string algorithms = DIGEST_ALGORITHMS + LEGACY_DIGEST_ALGORITHMS index = algorithms.map(&:downcase).index(string.downcase) index && algorithms[index] end end |
.digest_algorithms_list_text ⇒ Object
42 43 44 |
# File 'lib/file-digests.rb', line 42 def self.digest_algorithms_list_text "Digest algorithm should be one of the following: #{DIGEST_ALGORITHMS.join ", "}" end |
.parse_cli_options ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/file-digests.rb', line 46 def self. = {} OptionParser.new do |opts| opts. = [ "Usage: file-digests [options] [path/to/directory] [path/to/database_file]", " By default the current directory will be operated upon, and the database file will be placed to the current directory as well.", " Should you wish to check current directory but place the database elsewhere, you could provide \".\" as a first argument, and the path to a database_file as a second." ].join "\n" opts.on("-a", "--auto", "Do not ask for any confirmation.") do [:auto] = true end opts.on( "-d", "--digest DIGEST", 'Select a digest algorithm to use. Default is "BLAKE2b512".', 'You might also consider to use slower "SHA512-256" or even more slower "SHA3-256".', "#{digest_algorithms_list_text}.", "You only need to specify an algorithm on the first run, your choice will be saved to a database.", "Any time later you could specify a new algorithm to change the current one.", "Transition to a new algorithm will only occur if all files pass the check by digests which were stored using the old one." ) do |value| digest_algorithm = canonical_digest_algorithm_name(value) unless DIGEST_ALGORITHMS.include?(digest_algorithm) STDERR.puts "ERROR: #{digest_algorithms_list_text}" exit 1 end [:digest_algorithm] = digest_algorithm end opts.on("-f", "--accept-fate", "Accept the current state of files that are likely damaged and update their digest data.") do [:accept_fate] = true end opts.on("-h", "--help", "Prints this help.") do puts opts exit end opts.on("-p", "--duplicates", "Show the list of duplicate files, based on the information out of the database.") do [:action] = :show_duplicates end opts.on("-q", "--quiet", "Less verbose output, stil report any found issues.") do [:quiet] = true end opts.on( "-t", "--test", "Perform a test to verify directory contents.", "Compare actual files with the stored digests, check if any files are missing.", "Digest database will not be modified." ) do [:test_only] = true end opts.on("-v", "--verbose", "More verbose output.") do [:verbose] = true end end.parse! end |
.run_cli_utility ⇒ Object
111 112 113 114 115 116 117 |
# File 'lib/file-digests.rb', line 111 def self.run_cli_utility = file_digests = self.new ARGV[0], ARGV[1], file_digests.send([:action] || :perform_check) file_digests.close_database end |
Instance Method Details
#canonical_digest_algorithm_name(string) ⇒ Object
38 39 40 |
# File 'lib/file-digests.rb', line 38 def canonical_digest_algorithm_name string self.class.canonical_digest_algorithm_name string end |
#close_database ⇒ Object
213 214 215 216 217 |
# File 'lib/file-digests.rb', line 213 def close_database @statements.each(&:close) @db.close hide_database_files end |
#perform_check ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/file-digests.rb', line 143 def perform_check measure_time do perhaps_transaction(@new_digest_algorithm, :exclusive) do @counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0} walk_files(@files_path) do |filename| process_file filename end nested_transaction do puts "Tracking renames..." if @options[:verbose] track_renames end if any_missing_files? if any_exceptions? STDERR.puts "Due to previously occurred errors, missing files will not removed from the database." else report_missing_files if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database")) nested_transaction do puts "Removing missing files..." if @options[:verbose] remove_missing_files end end end end if @new_digest_algorithm && !@options[:test_only] if any_missing_files? || any_likely_damaged? || any_exceptions? STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception." else puts "Updating database to a new digest algorithm..." if @options[:verbose] digests_update_digests_to_new_digests "digest_algorithm", @new_digest_algorithm puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}" end end if any_likely_damaged? || any_exceptions? STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!" STDERR.puts "A list of errors is also saved in a file: #{@error_log_path}" end print_counters if any_missing_files? || any_likely_damaged? || any_exceptions? $FILE_DIGESTS_EXIT_STATUS=1 end end puts "Performing database maintenance..." if @options[:verbose] execute "PRAGMA optimize" execute "VACUUM" execute "PRAGMA wal_checkpoint(TRUNCATE)" end end |
#show_duplicates ⇒ Object
201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/file-digests.rb', line 201 def show_duplicates current_digest = nil digests_select_duplicates.each do |found| if current_digest != found["digest"] puts "" if current_digest current_digest = found["digest"] puts "#{found["digest"]}:" end puts " #{found["filename"]}" end end |