fingerprint 1.4.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +4 -0
- data/bin/fingerprint +6 -197
- data/lib/fingerprint/checker.rb +9 -9
- data/lib/fingerprint/checksums.rb +33 -0
- data/lib/fingerprint/command/analyze.rb +74 -0
- data/lib/fingerprint/command/compare.rb +55 -0
- data/lib/fingerprint/command/duplicates.rb +98 -0
- data/lib/fingerprint/command/scan.rb +61 -0
- data/lib/fingerprint/command/verify.rb +86 -0
- data/lib/fingerprint/command.rb +90 -0
- data/{test/test_fingerprint.rb → lib/fingerprint/find.rb} +16 -37
- data/lib/fingerprint/record.rb +20 -1
- data/lib/fingerprint/scanner.rb +137 -94
- data/lib/fingerprint/version.rb +2 -3
- data/lib/fingerprint.rb +16 -3
- data.tar.gz.sig +0 -0
- metadata +93 -42
- metadata.gz.sig +4 -0
- data/Gemfile +0 -4
- data/README.md +0 -73
- data/bin/fingerprint-diff +0 -145
- data/fingerprint.gemspec +0 -27
- data/rakefile.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 29779cfa264198432f16e421ed412e812a464332c4499d65e6a8039d0c08522f
|
4
|
+
data.tar.gz: 97dc135381bfa725ab41d34fbaf58ff56a16c89bb2bb8749b1539c90abe30cd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8f9fdb7259e57057bc10f9e412b0444393ab2bcd90d11b7091fffa40800759f2a52f7fcc25a2f8edafe8cebbc199ae6a81eb95401e2e2204f2bb28d1ab611d0
|
7
|
+
data.tar.gz: 418347660a6002e3e1cc5092a0e50c6d4462a034a6ccb1afe5e74f2912d2bb1d9dc3eda892fe4342a2db151d2952410461cbdde5f954fa6e881dc59e2df6d251
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
.P�v�������m���\Q=G �ivO,����/�.>e��'\����0H���$ѭ5X�����,D���J!�����Ҝ�+*��T04mPkTR�:>F -^��i`�/��G�w�ܺ�ź����F1A<>�J
|
2
|
+
�?&'�.�PvqsL���W���G�Q��p[��@�M�����P�,H�q�����S e>��u�_��K��r�� A��?�)g/�=��/�#����=-�R2�������a���^�,�=����> Y������7;K�tA��;���D��;� O��_�����B4�Y��ߕ�y%8sb�ͦ��x�
|
3
|
+
�C�S��xG�
|
4
|
+
[I�c/�[r+W>�&��Ӈ�~H E�R�K�Y"��l>:
|
data/bin/fingerprint
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
4
|
#
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -20,201 +20,10 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
require 'fileutils'
|
23
|
+
# This script takes a given path, and renames it with the given format.
|
24
|
+
# It then ensures that there is a symlink called "latest" that points
|
25
|
+
# to the renamed directory.
|
27
26
|
|
28
|
-
|
29
|
-
:root => "./",
|
30
|
-
:mode => :scan,
|
31
|
-
:output => $stdout,
|
32
|
-
:verbose => false,
|
33
|
-
:force => false,
|
34
|
-
:name => "index.fingerprint",
|
35
|
-
:extended => false,
|
36
|
-
:checksums => Fingerprint::DEFAULT_CHECKSUMS,
|
37
|
-
:additions => false,
|
38
|
-
:failures => :ignore,
|
39
|
-
}
|
27
|
+
require_relative '../lib/fingerprint/command'
|
40
28
|
|
41
|
-
|
42
|
-
script_name = File.basename($0)
|
43
|
-
|
44
|
-
o.banner = "Usage: #{script_name} [options] [path]"
|
45
|
-
o.define_head "This script is used to create and compare file system fingerprints."
|
46
|
-
|
47
|
-
o.separator ""
|
48
|
-
o.separator "Directory analysis and verification:"
|
49
|
-
|
50
|
-
o.on("-a", "--analyze [path]", String, "Generage a fingerprint of the given path and save it for later verification.") do |path|
|
51
|
-
OPTIONS[:mode] = :analyze
|
52
|
-
OPTIONS[:root] = path if path
|
53
|
-
end
|
54
|
-
|
55
|
-
o.on("-v", "--verify [path]", String, "Verify a given path based on a previously saved fingerprint.") do |path|
|
56
|
-
OPTIONS[:mode] = :verify
|
57
|
-
OPTIONS[:root] = path if path
|
58
|
-
end
|
59
|
-
|
60
|
-
o.on("-n name", String, "Specify the name of the fingerprint file.", "Default: #{OPTIONS[:name]}") do |name|
|
61
|
-
OPTIONS[:name] = name
|
62
|
-
end
|
63
|
-
|
64
|
-
o.on("-f", "Force any operation to complete despite warnings.") do
|
65
|
-
OPTIONS[:force] = true
|
66
|
-
end
|
67
|
-
|
68
|
-
o.separator ""
|
69
|
-
|
70
|
-
o.on("-x", "Include additional extended information about files and directories.") do
|
71
|
-
OPTIONS[:extended] = true
|
72
|
-
end
|
73
|
-
|
74
|
-
o.on("-s [checksum1,checksum2]", "Provide a list of the checksum algorithms to use.", "Available: #{Fingerprint::CHECKSUMS.keys.join(', ')}; Default: #{OPTIONS[:checksums].join(', ')}") do |checksums|
|
75
|
-
OPTIONS[:checksums] = checksums.split(/[\s,]+/)
|
76
|
-
end
|
77
|
-
|
78
|
-
o.separator ""
|
79
|
-
o.separator "Compare fingerprints:"
|
80
|
-
|
81
|
-
o.on("-c", "Compare the given fingerprints. Check that the second fingerprint is a superset of the first.") do
|
82
|
-
OPTIONS[:mode] = :check
|
83
|
-
end
|
84
|
-
|
85
|
-
o.on("-A", "Report files that have been added to the second fingerprint.") do
|
86
|
-
OPTIONS[:additions] = true
|
87
|
-
end
|
88
|
-
|
89
|
-
o.separator ""
|
90
|
-
o.separator "Output manipulation:"
|
91
|
-
|
92
|
-
o.on("-o [output-path]", String, "Write the fingerprint output to the given file.") do |path|
|
93
|
-
OPTIONS[:output] = File.open(path, "w")
|
94
|
-
end
|
95
|
-
|
96
|
-
o.on("--verbose", "Verbose output, include additional details in the file transcript.") do
|
97
|
-
OPTIONS[:verbose] = true
|
98
|
-
end
|
99
|
-
|
100
|
-
o.on("--progress", "Print percentage progress to standard error.") do
|
101
|
-
OPTIONS[:progress] = true
|
102
|
-
end
|
103
|
-
|
104
|
-
o.on("--die", "Give a non-zero exit code if errors are detected by check or verify.") do
|
105
|
-
OPTIONS[:failures] = :die
|
106
|
-
end
|
107
|
-
|
108
|
-
o.separator ""
|
109
|
-
o.separator "Help and Copyright information:"
|
110
|
-
|
111
|
-
o.on_tail("--copy", "Display copyright and warranty information") do
|
112
|
-
$stderr.puts "#{script_name} v#{Fingerprint::VERSION}. Copyright (c) 2011 Samuel Williams."
|
113
|
-
$stderr.puts "This software is released under the MIT license and comes with ABSOLUTELY NO WARRANTY."
|
114
|
-
$stderr.puts "See http://www.oriontransfer.co.nz/ for more information."
|
115
|
-
exit
|
116
|
-
end
|
117
|
-
|
118
|
-
o.on_tail("-h", "--help", "Show this help message.") do
|
119
|
-
$stderr.puts o
|
120
|
-
exit
|
121
|
-
end
|
122
|
-
end.parse!
|
123
|
-
|
124
|
-
unless File.directory? OPTIONS[:root]
|
125
|
-
$stderr.puts "Path #{OPTIONS[:root]} doesn't exist!"
|
126
|
-
exit(255)
|
127
|
-
end
|
128
|
-
|
129
|
-
if OPTIONS[:checksums].size == 0
|
130
|
-
OPTIONS[:checksums] = ['MD5', 'SHA2.256']
|
131
|
-
end
|
132
|
-
|
133
|
-
def finish_check(error_count)
|
134
|
-
if error_count == 0
|
135
|
-
$stderr.puts "Data verified, 0 errors found."
|
136
|
-
exit(0)
|
137
|
-
else
|
138
|
-
$stderr.puts "Data inconsistent, #{error_count} errors found!"
|
139
|
-
exit(OPTIONS[:failures] == :die ? 1 : 0)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
case (OPTIONS[:mode])
|
144
|
-
when :analyze
|
145
|
-
output_file = Pathname.new(OPTIONS[:root]) + OPTIONS[:name]
|
146
|
-
|
147
|
-
if output_file.exist? && !OPTIONS[:force]
|
148
|
-
$stderr.puts "Output file #{output_file} already exists. Aborting."
|
149
|
-
exit(2)
|
150
|
-
end
|
151
|
-
|
152
|
-
options = OPTIONS.dup
|
153
|
-
options[:excludes] = [OPTIONS[:name]]
|
154
|
-
|
155
|
-
finished = false
|
156
|
-
begin
|
157
|
-
File.open(output_file, "w") do |io|
|
158
|
-
options[:output] = io
|
159
|
-
|
160
|
-
Fingerprint::Scanner.scan_paths([OPTIONS[:root]], options)
|
161
|
-
end
|
162
|
-
finished = true
|
163
|
-
ensure
|
164
|
-
FileUtils.rm(output_file) unless finished
|
165
|
-
end
|
166
|
-
when :verify
|
167
|
-
error_count = 0
|
168
|
-
|
169
|
-
input_file = Pathname.new(OPTIONS[:root]) + OPTIONS[:name]
|
170
|
-
|
171
|
-
unless File.exist? input_file
|
172
|
-
$stderr.puts "Can't find index #{input_file}. Aborting."
|
173
|
-
exit(3)
|
174
|
-
end
|
175
|
-
|
176
|
-
options = OPTIONS.dup
|
177
|
-
|
178
|
-
master = Fingerprint::RecordSet.new
|
179
|
-
|
180
|
-
File.open(input_file, "r") do |io|
|
181
|
-
master.parse(io)
|
182
|
-
end
|
183
|
-
|
184
|
-
if master.configuration
|
185
|
-
options.merge!(master.configuration.options)
|
186
|
-
end
|
187
|
-
|
188
|
-
scanner = Fingerprint::Scanner.new([OPTIONS[:root]], options)
|
189
|
-
copy = Fingerprint::SparseRecordSet.new(scanner)
|
190
|
-
|
191
|
-
error_count += Fingerprint::Checker::verify(master, copy, options)
|
192
|
-
|
193
|
-
finish_check(error_count)
|
194
|
-
when :scan
|
195
|
-
roots = ARGV
|
196
|
-
roots << Dir.pwd if roots.size == 0
|
197
|
-
|
198
|
-
# Check that all supplied paths exist
|
199
|
-
roots.delete_if do |root|
|
200
|
-
if File.exist? root
|
201
|
-
false
|
202
|
-
else
|
203
|
-
$stderr.puts "Path #{root} doesn't exist, skipping!"
|
204
|
-
true
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
options = OPTIONS.dup
|
209
|
-
|
210
|
-
# Discard output once it has been written to disk:
|
211
|
-
options[:recordset] = nil
|
212
|
-
|
213
|
-
Fingerprint::Scanner.scan_paths(roots, options)
|
214
|
-
when :check
|
215
|
-
options = OPTIONS.dup
|
216
|
-
|
217
|
-
error_count = Fingerprint::Checker.check_files(ARGV[0], ARGV[1], options)
|
218
|
-
|
219
|
-
finish_check(error_count)
|
220
|
-
end
|
29
|
+
Fingerprint::Command.call
|
data/lib/fingerprint/checker.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright, 2011, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
21
|
+
require_relative 'record'
|
22
22
|
|
23
23
|
module Fingerprint
|
24
24
|
# Given two fingerprints (master and copy) ensures that the copy has at least everything contained
|
@@ -31,7 +31,7 @@ module Fingerprint
|
|
31
31
|
#
|
32
32
|
# Master and copy are +IO+ objects corresponding to the output produced by +Fingerprint::Scanner+.
|
33
33
|
class Checker
|
34
|
-
def initialize(master, copy, options
|
34
|
+
def initialize(master, copy, **options)
|
35
35
|
@master = master
|
36
36
|
@copy = copy
|
37
37
|
|
@@ -42,7 +42,7 @@ module Fingerprint
|
|
42
42
|
attr :copy
|
43
43
|
|
44
44
|
# Run the checking process.
|
45
|
-
def check
|
45
|
+
def check(&block)
|
46
46
|
# For every file in the src, we check that it exists
|
47
47
|
# in the destination:
|
48
48
|
total_count = @master.records.count
|
@@ -77,7 +77,7 @@ module Fingerprint
|
|
77
77
|
end
|
78
78
|
|
79
79
|
if @options[:progress]
|
80
|
-
$stderr.puts "# Progress: File #{processed_count} / #{total_count}; Byte #{processed_size} / #{total_size} = #{sprintf('%0.2f
|
80
|
+
$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)}"
|
81
81
|
|
82
82
|
processed_size += (record['file.size'] || 0).to_i
|
83
83
|
end
|
@@ -95,7 +95,7 @@ module Fingerprint
|
|
95
95
|
# A list of files which either did not exist in the copy, or had the wrong checksum.
|
96
96
|
attr :failures
|
97
97
|
|
98
|
-
def self.check_files(master, copy, options
|
98
|
+
def self.check_files(master, copy, **options, &block)
|
99
99
|
# New API that takes two RecordSets...
|
100
100
|
|
101
101
|
File.open(master) do |master_file|
|
@@ -106,13 +106,13 @@ module Fingerprint
|
|
106
106
|
copy_recordset = RecordSet.new
|
107
107
|
copy_recordset.parse(copy_file)
|
108
108
|
|
109
|
-
verify(master_recordset, copy_recordset, options, &block)
|
109
|
+
verify(master_recordset, copy_recordset, **options, &block)
|
110
110
|
end
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
114
|
# Helper function to check two fingerprint files.
|
115
|
-
def self.verify(master, copy, options
|
115
|
+
def self.verify(master, copy, **options, &block)
|
116
116
|
error_count = 0
|
117
117
|
|
118
118
|
errors = options.delete(:recordset) || RecordSet.new
|
@@ -120,7 +120,7 @@ module Fingerprint
|
|
120
120
|
errors = RecordSetPrinter.new(errors, options[:output])
|
121
121
|
end
|
122
122
|
|
123
|
-
checker = Checker.new(master, copy, options)
|
123
|
+
checker = Checker.new(master, copy, **options)
|
124
124
|
|
125
125
|
checker.check do |record, result, message|
|
126
126
|
error_count += 1
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# This script takes a given path, and renames it with the given format.
|
22
|
+
# It then ensures that there is a symlink called "latest" that points
|
23
|
+
# to the renamed directory.
|
24
|
+
|
25
|
+
require 'samovar'
|
26
|
+
|
27
|
+
module Fingerprint
|
28
|
+
module Checksums
|
29
|
+
def self.call(result)
|
30
|
+
result.split(/\s*,\s*/)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# This script takes a given path, and renames it with the given format.
|
22
|
+
# It then ensures that there is a symlink called "latest" that points
|
23
|
+
# to the renamed directory.
|
24
|
+
|
25
|
+
require 'samovar'
|
26
|
+
require 'fileutils'
|
27
|
+
|
28
|
+
require_relative '../checksums'
|
29
|
+
require_relative '../scanner'
|
30
|
+
require_relative '../record'
|
31
|
+
|
32
|
+
module Fingerprint
|
33
|
+
module Command
|
34
|
+
class Analyze < Samovar::Command
|
35
|
+
self.description = "Generates a fingerprint for the specified paths and saves it."
|
36
|
+
|
37
|
+
options do
|
38
|
+
option "-n/--name <name>", "The fingerprint file name.", default: INDEX_FINGERPRINT
|
39
|
+
|
40
|
+
option "-f/--force", "Force all operations to complete despite warnings."
|
41
|
+
option "-x/--extended", "Include extended information about files and directories."
|
42
|
+
option "-s/--checksums <SHA2.256>", "Specify what checksum algorithms to use: #{CHECKSUMS.keys.join(', ')}.", default: DEFAULT_CHECKSUMS, type: Checksums
|
43
|
+
|
44
|
+
option "--progress", "Print structured progress to standard error."
|
45
|
+
option "--verbose", "Verbose fingerprint output, e.g. excluded paths."
|
46
|
+
end
|
47
|
+
|
48
|
+
many :paths, "Paths relative to the root to use for verification, or pwd if not specified.", default: ["./"]
|
49
|
+
|
50
|
+
def call
|
51
|
+
output_file = @options[:name]
|
52
|
+
|
53
|
+
if File.exist?(output_file) and !@options[:force]
|
54
|
+
abort "Output file #{output_file} already exists. Aborting."
|
55
|
+
end
|
56
|
+
|
57
|
+
options = @options.dup
|
58
|
+
options[:excludes] = [File.expand_path(options[:name], Dir.pwd)]
|
59
|
+
|
60
|
+
finished = false
|
61
|
+
begin
|
62
|
+
File.open(output_file, "w") do |io|
|
63
|
+
options[:output] = io
|
64
|
+
|
65
|
+
Scanner.scan_paths(@paths, **options)
|
66
|
+
end
|
67
|
+
finished = true
|
68
|
+
ensure
|
69
|
+
FileUtils.rm(output_file) unless finished
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# This script takes a given path, and renames it with the given format.
|
22
|
+
# It then ensures that there is a symlink called "latest" that points
|
23
|
+
# to the renamed directory.
|
24
|
+
|
25
|
+
require 'samovar'
|
26
|
+
|
27
|
+
module Fingerprint
|
28
|
+
module Command
|
29
|
+
class Compare < Samovar::Command
|
30
|
+
self.description = "Compare two fingerprints and report additions, removals and changes."
|
31
|
+
|
32
|
+
options do
|
33
|
+
option "-x/--extended", "Include extended information about files and directories."
|
34
|
+
option "-a/--additions", "Report files that have been added to the copy."
|
35
|
+
option "--fail-on-errors", "Exit with non-zero status if errors are encountered."
|
36
|
+
|
37
|
+
option "--progress", "Print structured progress to standard error."
|
38
|
+
end
|
39
|
+
|
40
|
+
one :master, "The fingerprint which represents the original data."
|
41
|
+
one :copy, "The fingerprint which represents a copy of the data."
|
42
|
+
|
43
|
+
def call
|
44
|
+
options = @options.dup
|
45
|
+
options[:output] = @parent.output
|
46
|
+
|
47
|
+
error_count = Checker.check_files(@master, @copy, **options)
|
48
|
+
|
49
|
+
if @options[:fail_on_errors]
|
50
|
+
abort "Data inconsistent, #{error_count} error(s) found!" if error_count != 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# This script takes a given path, and renames it with the given format.
|
22
|
+
# It then ensures that there is a symlink called "latest" that points
|
23
|
+
# to the renamed directory.
|
24
|
+
|
25
|
+
require 'samovar'
|
26
|
+
|
27
|
+
module Fingerprint
|
28
|
+
module Command
|
29
|
+
class Duplicates < Samovar::Command
|
30
|
+
self.description = "Efficiently find duplicates in a given fingerprint."
|
31
|
+
|
32
|
+
options do
|
33
|
+
option "-i/--inverse", "Invert the output, i.e. show files which are not duplicates."
|
34
|
+
option "-x/--extended", "Include extended information about files and directories."
|
35
|
+
|
36
|
+
option "--verbose", "Verbose output, e.g. what is happening."
|
37
|
+
end
|
38
|
+
|
39
|
+
one :master, "The source fingerprint which represents the primarily file list."
|
40
|
+
many :copies, "Zero or more fingerprints which might contain duplicates.", default: []
|
41
|
+
|
42
|
+
attr :duplicates_recordset
|
43
|
+
|
44
|
+
def call
|
45
|
+
@options[:output] = @parent.output
|
46
|
+
|
47
|
+
@duplicates_recordset = RecordSet.new
|
48
|
+
results = RecordSetPrinter.new(duplicates_recordset, @options[:output])
|
49
|
+
|
50
|
+
master_file_path = @master
|
51
|
+
File.open(master_file_path) do |master_file|
|
52
|
+
master_recordset = RecordSet.new
|
53
|
+
master_recordset.parse(master_file)
|
54
|
+
|
55
|
+
ignore_similar = false
|
56
|
+
|
57
|
+
copy_file_paths = @copies
|
58
|
+
|
59
|
+
if copy_file_paths.size == 0
|
60
|
+
copy_file_paths = [master_file_path]
|
61
|
+
ignore_similar = true
|
62
|
+
end
|
63
|
+
|
64
|
+
copy_file_paths.each do |copy_file_path|
|
65
|
+
File.open(copy_file_path) do |copy_file|
|
66
|
+
copy_recordset = RecordSet.new
|
67
|
+
copy_recordset.parse(copy_file)
|
68
|
+
|
69
|
+
copy_recordset.records.each do |record|
|
70
|
+
record.metadata['fingerprint'] = copy_file_path
|
71
|
+
# We need to see if the record exists in the master
|
72
|
+
|
73
|
+
if @options[:verbose]
|
74
|
+
$stderr.puts "Checking #{record.inspect}"
|
75
|
+
end
|
76
|
+
|
77
|
+
main_record = master_recordset.find_by_key(record)
|
78
|
+
|
79
|
+
# If we are scanning the same index, don't print out every file, just those that are duplicates within the single file.
|
80
|
+
if ignore_similar && main_record && (main_record.path == record.path)
|
81
|
+
main_record = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
if main_record
|
85
|
+
record.metadata['original.path'] = main_record.path
|
86
|
+
record.metadata['original.fingerprint'] = master_file_path
|
87
|
+
results << record if !@options[:inverse]
|
88
|
+
else
|
89
|
+
results << record if @options[:inverse]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# This script takes a given path, and renames it with the given format.
|
22
|
+
# It then ensures that there is a symlink called "latest" that points
|
23
|
+
# to the renamed directory.
|
24
|
+
|
25
|
+
require 'samovar'
|
26
|
+
|
27
|
+
require_relative '../checksums'
|
28
|
+
require_relative '../scanner'
|
29
|
+
require_relative '../record'
|
30
|
+
|
31
|
+
module Fingerprint
|
32
|
+
module Command
|
33
|
+
class Scan < Samovar::Command
|
34
|
+
self.description = "Generate a fingerprint from the given paths."
|
35
|
+
|
36
|
+
options do
|
37
|
+
option "-p/--path <path>", "Analyze the given path relative to root.", default: "./"
|
38
|
+
|
39
|
+
option "-x/--extended", "Include extended information about files and directories."
|
40
|
+
option "-s/--checksums <SHA2.256>", "Specify what checksum algorithms to use: #{CHECKSUMS.keys.join(', ')}.", default: DEFAULT_CHECKSUMS, type: Checksums
|
41
|
+
|
42
|
+
option "--progress", "Print structured progress to standard error."
|
43
|
+
option "--verbose", "Verbose fingerprint output, e.g. excluded paths."
|
44
|
+
end
|
45
|
+
|
46
|
+
many :paths, "Paths to scan."
|
47
|
+
|
48
|
+
def call
|
49
|
+
@paths = [Dir.pwd] unless @paths
|
50
|
+
|
51
|
+
options = @options.dup
|
52
|
+
|
53
|
+
# This configuration ensures that the output is printed to $stdout.
|
54
|
+
options[:output] = @parent.output
|
55
|
+
options[:recordset] = nil
|
56
|
+
|
57
|
+
Scanner.scan_paths(@paths, **options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|