fingerprint 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -26,6 +26,7 @@ Todo
26
26
  * Supporting tools for signing fingerprints easily.
27
27
  * Support indexing specific files as well as whole directories (maybe?).
28
28
  * Support general filenames for `--archive`, e.g. along with `-n`, maybe support a file called `index.fingerprint` by default: improved visibility for end user.
29
+ * Because fingerprint is currently IO bound in terms of performance, single-threaded checksumming is fine, but for SSD and other fast storage, it might be possible to improve speed somewhat by using a map-reduce style approach.
29
30
 
30
31
  License
31
32
  -------
@@ -48,4 +49,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
49
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
50
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
51
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51
- THE SOFTWARE.
52
+ THE SOFTWARE.
@@ -23,6 +23,8 @@
23
23
  require 'optparse'
24
24
  require 'pathname'
25
25
  require 'fingerprint'
26
+ require 'lockfile'
27
+ require 'fileutils'
26
28
 
27
29
  OPTIONS = {
28
30
  :root => "./",
@@ -30,9 +32,10 @@ OPTIONS = {
30
32
  :output => $stdout,
31
33
  :verbose => false,
32
34
  :force => false,
33
- :name => "._index.fingerprint",
35
+ :name => "index.fingerprint",
34
36
  :extended => false,
35
37
  :checksums => Fingerprint::DEFAULT_CHECKSUMS,
38
+ :lockfile => true
36
39
  }
37
40
 
38
41
  ARGV.options do |o|
@@ -80,6 +83,10 @@ ARGV.options do |o|
80
83
  o.on("--progress", "Print percentage progress to standard error.") do
81
84
  OPTIONS[:progress] = true
82
85
  end
86
+
87
+ o.on("--no-lockfile", "Don't use a lockfile to validate access to the fingerprint.") do
88
+ OPTIONS[:lockfile] = false
89
+ end
83
90
 
84
91
  o.separator ""
85
92
 
@@ -116,22 +123,61 @@ if OPTIONS[:checksums].size == 0
116
123
  OPTIONS[:checksums] = ['MD5', 'SHA2.256']
117
124
  end
118
125
 
126
+ # Run some block with the given lock, if requested.
127
+ def with_lock
128
+ lockfile_path = nil
129
+ if OPTIONS[:lockfile] == true
130
+ lockfile_path = Pathname.new(OPTIONS[:root]) + (OPTIONS[:name] + ".lock")
131
+ elsif OPTION[:lockfile] != false
132
+ lockfile_path = OPTIONS[:lockfile]
133
+ end
134
+
135
+ if lockfile_path
136
+ begin
137
+ Lockfile.new(lockfile_path, :retries => 0) do
138
+ yield
139
+ end
140
+ rescue Lockfile::MaxTriesLockError
141
+ $stderr.puts "Could not acquire lock #{lockfile_path}."
142
+
143
+ return false
144
+ end
145
+ else
146
+ yield
147
+ end
148
+
149
+ return true
150
+ end
151
+
119
152
  case (OPTIONS[:mode])
120
153
  when :analyze
121
- output_file = Pathname.new(OPTIONS[:root]) + OPTIONS[:name]
122
-
123
- if output_file.exist? && !OPTIONS[:force]
124
- $stderr.puts "Output file #{output_file} already exists. Aborting."
125
- exit(2)
126
- end
154
+ result = with_lock do
155
+ output_file = Pathname.new(OPTIONS[:root]) + OPTIONS[:name]
127
156
 
128
- options = OPTIONS.dup
129
- options[:excludes] = [OPTIONS[:name]]
157
+ if output_file.exist? && !OPTIONS[:force]
158
+ $stderr.puts "Output file #{output_file} already exists. Aborting."
159
+ exit(2)
160
+ end
130
161
 
131
- File.open(output_file, "w") do |io|
132
- options[:output] = io
162
+ options = OPTIONS.dup
163
+ options[:excludes] = [OPTIONS[:name]]
133
164
 
134
- Fingerprint::Scanner.scan_paths([OPTIONS[:root]], options)
165
+ finished = false
166
+ begin
167
+ File.open(output_file, "w") do |io|
168
+ options[:output] = io
169
+
170
+ Fingerprint::Scanner.scan_paths([OPTIONS[:root]], options)
171
+ end
172
+ finished = true
173
+ ensure
174
+ FileUtils.rm(output_file) unless finished
175
+ end
176
+ end
177
+
178
+ unless result
179
+ # Lockfile failure
180
+ exit(4)
135
181
  end
136
182
  when :verify
137
183
  error_count = 0
@@ -32,7 +32,7 @@ module Fingerprint
32
32
  }
33
33
 
34
34
  class Record
35
- def initialize(mode, path, metadata)
35
+ def initialize(mode, path, metadata = {})
36
36
  @mode = mode
37
37
  @path = path
38
38
 
@@ -46,7 +46,9 @@ module Fingerprint
46
46
  @options = options
47
47
 
48
48
  @digests = {}
49
-
49
+
50
+ @progress = nil
51
+
50
52
  unless @options[:checksums] and @options[:checksums].size > 0
51
53
  @options[:checksums] = DEFAULT_CHECKSUMS
52
54
  end
@@ -75,6 +77,8 @@ module Fingerprint
75
77
 
76
78
  # This code won't handle multiple threads..
77
79
  def digests_for(path)
80
+ total = 0
81
+
78
82
  @digests.each do |key, digest|
79
83
  digest.reset
80
84
  end
@@ -82,6 +86,10 @@ module Fingerprint
82
86
  File.open(path, "rb") do |file|
83
87
  buf = ""
84
88
  while file.read(1024 * 1024 * 10, buf)
89
+ total += buf.size
90
+
91
+ @progress.call(total) if @progress
92
+
85
93
  @digests.each do |key, digest|
86
94
  digest << buf
87
95
  end
@@ -104,6 +112,7 @@ module Fingerprint
104
112
  if type == :file
105
113
  metadata['file.size'] = stat.size
106
114
  digests = digests_for(path)
115
+ metadata.merge!(digests)
107
116
  end
108
117
 
109
118
  # Extended information
@@ -129,7 +138,9 @@ module Fingerprint
129
138
  # Output a file and associated metadata.
130
139
  def file_record_for(path)
131
140
  metadata = metadata_for(:file, path)
132
- metadata.merge!(digests_for(path))
141
+
142
+ # Should this be here or in metadata_for?
143
+ # metadata.merge!(digests_for(path))
133
144
 
134
145
  Record.new(:file, path, metadata)
135
146
  end
@@ -183,6 +194,10 @@ module Fingerprint
183
194
  @roots.each do |root|
184
195
  Dir.chdir(root) do
185
196
  Find.find("./") do |path|
197
+ if @options[:progress]
198
+ $stderr.puts "# Scanning: #{path}"
199
+ end
200
+
186
201
  if File.directory?(path)
187
202
  if excluded?(path)
188
203
  Find.prune # Ignore this directory
@@ -199,11 +214,21 @@ module Fingerprint
199
214
  end
200
215
  end
201
216
 
217
+ if @options[:progress]
218
+ @progress = lambda do |read_size|
219
+ $stderr.puts "# Progress: File #{processed_count} / #{total_count}; Byte #{processed_size + read_size} / #{total_size} = #{sprintf('%0.3f%', (processed_size + read_size).to_f / total_size.to_f * 100.0)} (#{read_size}, #{processed_size}, #{total_size})"
220
+ end
221
+ end
222
+
202
223
  @roots.each do |root|
203
224
  Dir.chdir(root) do
204
225
  recordset << header_for(root)
205
226
 
206
227
  Find.find("./") do |path|
228
+ if @options[:progress]
229
+ $stderr.puts "# Path: #{path}"
230
+ end
231
+
207
232
  if File.directory?(path)
208
233
  if excluded?(path)
209
234
  excluded_count += 1
@@ -221,10 +246,10 @@ module Fingerprint
221
246
  else
222
247
  # Skip anything that isn't a valid file (e.g. pipes, sockets, symlinks).
223
248
  if valid_file?(path)
249
+ recordset << file_record_for(path)
250
+
224
251
  processed_count += 1
225
252
  processed_size += File.size(path)
226
-
227
- recordset << file_record_for(path)
228
253
  else
229
254
  excluded_count += 1
230
255
 
@@ -235,9 +260,7 @@ module Fingerprint
235
260
  end
236
261
 
237
262
  # Print out a progress summary if requested
238
- if @options[:progress]
239
- $stderr.puts "# Progress: File #{processed_count} / #{total_count} = #{sprintf('%0.2f%', processed_count.to_f / total_count.to_f * 100.0)}; Byte #{processed_size} / #{total_size} = #{sprintf('%0.2f%', processed_size.to_f / total_size.to_f * 100.0)}"
240
- end
263
+ @progress.call(0) if @progress
241
264
  end
242
265
  end
243
266
  end
@@ -23,7 +23,7 @@ module Fingerprint
23
23
  module VERSION
24
24
  MAJOR = 1
25
25
  MINOR = 3
26
- TINY = 0
26
+ TINY = 1
27
27
 
28
28
  STRING = [MAJOR, MINOR, TINY].join('.')
29
29
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fingerprint
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 0
10
- version: 1.3.0
9
+ - 1
10
+ version: 1.3.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Samuel Williams
@@ -15,9 +15,22 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-18 00:00:00 Z
19
- dependencies: []
20
-
18
+ date: 2011-10-05 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: lockfile
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
21
34
  description:
22
35
  email: samuel@oriontransfer.org
23
36
  executables: