file_pool 0.5.1 → 0.6.2

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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/file_pool/version.rb +1 -1
  3. data/lib/file_pool.rb +174 -57
  4. metadata +49 -76
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 133469293b9b495e160951b1abd81d5716aa06b1e9abb18a0f1b98b6904f3ad8
4
+ data.tar.gz: 1b4befbb63377f359174c7e77caf5e95a831412d709efa688dc1cdfb6b2daf5b
5
+ SHA512:
6
+ metadata.gz: 28b215968194546b52830604ac034616a2a78d55db738a83d91ac8e2a5ffaf703e658e4535e1865dc37b102026725a3119770c0ac8f4f346d0aed43387be78ad
7
+ data.tar.gz: f3345fd4c4a9e71dc4a3f02e499ae1b85c2bc4dd4ad9a5c65a573f85f5c99cbe17ac850123cf5bad7dab79f167e2da0e5bc81536be583da9057371f9015c0fb4
@@ -1,3 +1,3 @@
1
1
  module FilePool
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.2"
3
3
  end
data/lib/file_pool.rb CHANGED
@@ -27,20 +27,20 @@ module FilePool
27
27
  # encryption/decryption in bytes. Larger blocks need more memory and less time (less IO).
28
28
  # Defaults to 1'048'576 (1 MiB).
29
29
  # * :copy_source (true,false)
30
- # if +false+ files added to the pool are hard-linked with the source if source and file pool
30
+ # if +false+ files added to the pool are hard-linked with the source if source and file pool
31
31
  # are on the same file system (default). If set to +true+ files are always copied into the pool.
32
32
  # * :mode (Integer)
33
33
  # File mode to set on all files added to the pool. E.g. +mode:+ +0640+ for +rw-r-----+ or symbolic "u=wrx,go=rx"
34
34
  # (see Ruby stdlib FileUtils#chmod).
35
- # Note that the desired mode is not set if the file is hard-linked with the source.
35
+ # Note that the desired mode is not set if the file is hard-linked with the source.
36
36
  # Use +copy_source:true+ when to ensure.
37
37
  # * :owner
38
- # Owner of the files added to the pool.
39
- # Note that the desired owner is not set if the file is hard-linked with the source.
38
+ # Owner of the files added to the pool.
39
+ # Note that the desired owner is not set if the file is hard-linked with the source.
40
40
  # Use +copy_source:true+ when to ensure.
41
41
  # * :group
42
- # Group of the files added to the pool.
43
- # Note that the desired group is not set if the file is hard-linked with the source.
42
+ # Group of the files added to the pool.
43
+ # Note that the desired group is not set if the file is hard-linked with the source.
44
44
  # Use +copy_source:true+ when to ensure.
45
45
  def self.setup root, options={}
46
46
  unless(unknown = options.keys - [:encryption_block_size, :secrets_file, :copy_source, :mode, :owner, :group]).empty?
@@ -70,32 +70,50 @@ module FilePool
70
70
  #
71
71
  # path (String)::
72
72
  # path of the file to add.
73
+ # options (Hash)::
74
+ # :background (true,false) adding large files can take long (esp. with encryption), +true+ won't block, default is +false+
73
75
  #
74
76
  # === Return Value:
75
77
  #
76
78
  # :: *String* containing a new unique ID for the file added.
77
- def self.add! orig_path
79
+ def self.add! orig_path, options = {}
78
80
  newid = uuid
79
- target = path newid
80
81
 
81
- if @@crypted_mode
82
- FileUtils.mkpath(id2dir_secured newid)
83
- path = crypt(orig_path)
84
- else
85
- path = orig_path
86
- FileUtils.mkpath(id2dir newid)
87
- end
82
+ raise Errno::ENOENT unless File.exist?(orig_path)
88
83
 
89
- if !@@copy_source and (File.stat(path).dev == File.stat(File.dirname(target)).dev)
90
- FileUtils.link(path, target)
91
- else
92
- FileUtils.copy(path, target)
84
+ child = fork do
85
+ target = path newid
86
+
87
+ if @@crypted_mode
88
+ FileUtils.mkpath(id2dir_secured newid)
89
+ path = encrypt_to_tempfile(orig_path)
90
+ else
91
+ path = orig_path
92
+ FileUtils.mkpath(id2dir newid)
93
+ end
94
+
95
+ # try quick hard-linking, copy if forced or hard-linking impossible
96
+ begin
97
+ raise Errno::EXDEV if @@copy_source
98
+ FileUtils.link(path, target)
99
+ rescue Errno::EXDEV
100
+ FileUtils.copy(path, target)
101
+ end
102
+
103
+ # don't chmod if orginal file is same as target (hard-linked)
104
+ if File.stat(orig_path).ino != File.stat(File.dirname(target)).ino
105
+ FileUtils.chmod(@@mode, target) if @@mode
106
+ FileUtils.chown(@@owner, @@group, target)
107
+ end
93
108
  end
94
109
 
95
- # don't chmod if orginal file is same as target (hard-linked)
96
- if File.stat(orig_path).ino != File.stat(File.dirname(target)).ino
97
- FileUtils.chmod(@@mode, target) if @@mode
98
- FileUtils.chown(@@owner, @@group, target)
110
+
111
+ if options[:background]
112
+ # don't wait, avoid zombies
113
+ Process.detach(child)
114
+ else
115
+ # block until done
116
+ Process.waitpid(child)
99
117
  end
100
118
 
101
119
  newid
@@ -110,18 +128,49 @@ module FilePool
110
128
  #
111
129
  # source (String)::
112
130
  # path of the file to add.
131
+ # options (Hash)::
132
+ # :background (true,false) adding large files can take long (esp. with encryption), +true+ won't block, default is +false+
113
133
  #
114
134
  # === Return Value:
115
135
  #
116
136
  # :: *String* containing a new unique ID for the file added.
117
137
  # :: +false+ when the file could not be stored.
118
- def self.add path
119
- self.add!(path)
138
+ def self.add path, options = {}
139
+ self.add!(path, options)
120
140
 
121
141
  rescue Exception
122
142
  return false
123
143
  end
124
144
 
145
+ #
146
+ # Add a new file from a stream to the file pool
147
+ #
148
+ def self.add_stream in_stream
149
+ newid = uuid
150
+
151
+ # ensure target path exists
152
+ if @@crypted_mode
153
+ FileUtils.mkpath(id2dir_secured newid)
154
+ else
155
+ FileUtils.mkpath(id2dir newid)
156
+ end
157
+
158
+ child = fork do
159
+ target = path(newid)
160
+ # create the target file to write
161
+ open(target,'w') do |out_stream|
162
+ encrypt in_stream, out_stream
163
+ end
164
+
165
+ FileUtils.chmod(@@mode, target) if @@mode
166
+ FileUtils.chown(@@owner, @@group, target)
167
+ end
168
+
169
+ Process.detach(child)
170
+
171
+ newid
172
+ end
173
+
125
174
  #
126
175
  # Return the file's path corresponding to the passed file ID, no matter if it
127
176
  # exists or not. In encrypting mode the file is first decrypted and the
@@ -141,7 +190,7 @@ module FilePool
141
190
  #
142
191
  # :: *String*, absolute path of the file in the pool or to temporary location if it was decrypted.
143
192
  def self.path fid, options={}
144
- options[:decrypt] = true unless options[:decrypt] == false
193
+ options[:decrypt] = options.fetch(:decrypt, true)
145
194
 
146
195
  raise InvalidFileId unless valid?(fid)
147
196
 
@@ -151,7 +200,7 @@ module FilePool
151
200
  if @@crypted_mode
152
201
  if options[:decrypt]
153
202
  # return path of decrypted file (tmp path)
154
- decrypt id2dir_secured(fid) + "/#{fid}"
203
+ decrypt_to_tempfile id2dir_secured(fid) + "/#{fid}"
155
204
  else
156
205
  id2dir_secured(fid) + "/#{fid}"
157
206
  end
@@ -171,6 +220,33 @@ module FilePool
171
220
  end
172
221
  end
173
222
 
223
+ # Returns an IO object providing an (unencrypted) stream of data of the given file ID
224
+ # To get the stream of the encrypted data pass :decrypt => false, as an option.
225
+ #
226
+ # === Parameters:
227
+ #
228
+ # fid (String)::
229
+ # File ID which was generated by a previous #add operation.
230
+ #
231
+ # options (Hash)::
232
+ # :decrypt (true,false) In encryption mode don't decrypt, but prioved the encrypted data. Defaults to +true+.
233
+ #
234
+ # === Return Value:
235
+ #
236
+ # :: *IO*, IO stream open for reading
237
+ #
238
+ def self.stream fid, options={}
239
+ options[:decrypt] = options.fetch(:decrypt, true)
240
+
241
+ if path = path(fid, :decrypt => false)
242
+ if @@crypted_mode and options[:decrypt]
243
+ decrypt_to_stream path
244
+ else
245
+ open(path)
246
+ end
247
+ end
248
+ end
249
+
174
250
  #
175
251
  # Remove a previously added file by its ID. Same as FilePool.remove,
176
252
  # but throws exceptions on failure.
@@ -271,7 +347,7 @@ module FilePool
271
347
  end
272
348
 
273
349
  #
274
- # Crypt a file and store the result in the temp.
350
+ # Crypt a file and store the result a Tempfile
275
351
  #
276
352
  # Returns the path to the crypted file.
277
353
  #
@@ -282,28 +358,19 @@ module FilePool
282
358
  #
283
359
  # === Return Value:
284
360
  #
285
- # :: *String*Path and name of the crypted file.
286
- def self.crypt path
361
+ # :: *String* Path and name of the crypted file.
362
+ def self.encrypt_to_tempfile path
287
363
  # Crypt the file in the temp folder and copy after
288
- cipher = create_cipher
289
- result = Tempfile.new 'FilePool-encrypt'
290
-
291
- result.set_encoding Encoding::BINARY,Encoding::BINARY
292
- buf = ''.encode(Encoding::BINARY)
364
+ tempfile = Tempfile.new 'FilePool-encrypt'
365
+ tempfile.sync = true
293
366
 
294
- File.open(path) do |inf|
295
- while inf.read(@@block_size, buf)
296
- result << cipher.update(buf)
297
- end
298
- result << cipher.final
299
- end
367
+ encrypt open(path), tempfile
300
368
 
301
- result.close
302
- result.path
369
+ tempfile.path
303
370
  end
304
371
 
305
372
  #
306
- # Decrypt a file and give a path to it.
373
+ # Decrypt file to a file in /tmp
307
374
  #
308
375
  # Returns the path to the decrypted file.
309
376
  #
@@ -314,26 +381,76 @@ module FilePool
314
381
  #
315
382
  # === Return Value:
316
383
  #
317
- # :: *String*Path and name of the crypted file.
318
- def self.decrypt path
319
- decipher = create_decipher
320
- # Now decrypt the data:
321
- output = Tempfile.new 'FilePool-decrypt'
384
+ # :: *String* Path and name of the decrypted file.
385
+ def self.decrypt_to_tempfile path
386
+ tmpfile = Tempfile.new 'FilePool-decrypt'
387
+ tmpfile.sync = true
322
388
 
323
- output.set_encoding Encoding::BINARY,Encoding::BINARY
324
- buf = ''.encode(Encoding::BINARY)
389
+ File.open(path) do |encrypted_file|
390
+ decrypt encrypted_file, tmpfile
391
+ end
392
+
393
+ tmpfile.path
394
+ end
325
395
 
326
- File.open(path) do |inf|
327
- while inf.read(@@block_size, buf)
328
- output << decipher.update(buf)
396
+ #
397
+ # Decrypt file to a stream (IO) non-blocking by forking a child process.
398
+ #
399
+ # === Parameters:
400
+ #
401
+ # path (String)::
402
+ # path of the file to decrypt.
403
+ #
404
+ # === Return Value:
405
+ #
406
+ # :: *IO* decrypted data as stream
407
+ def self.decrypt_to_stream path
408
+
409
+ pipe_read, pipe_write = IO.pipe
410
+
411
+ child = fork do
412
+ # in child process: decrypting
413
+ pipe_read.close
414
+
415
+ # open encrypted file
416
+ File.open(path) do |encrypted_file|
417
+ decrypt encrypted_file, pipe_write
329
418
  end
330
- output << decipher.final
419
+
420
+ pipe_write.close
331
421
  end
332
422
 
333
- output.open # re-open for reading, prevents early deletion of tempfile
334
- output.path
423
+ # in parent
424
+ Process.detach(child) # not waiting for it
425
+ pipe_write.close
426
+ pipe_read
335
427
  end
336
428
 
429
+
430
+ # decrypts data stream +in_stream+ (IO: readable) to +out+ (IO: writeable), blocking
431
+ def self.decrypt in_stream, out_stream
432
+ crypt(in_stream, out_stream, :decrypt)
433
+ end
434
+
435
+ # encrypts data stream +in+ (IO: readable) to +out+ (IO: writeable), blocking
436
+ def self.encrypt in_stream, out_stream
437
+ crypt(in_stream, out_stream, :encrypt)
438
+ end
439
+
440
+ def self.crypt in_stream, out_stream, direction
441
+ cipher = (direction == :encrypt) ? create_cipher : create_decipher
442
+
443
+ buf = ''.encode(Encoding::BINARY)
444
+ out_stream.set_encoding Encoding::BINARY,Encoding::BINARY
445
+ in_stream.set_encoding Encoding::BINARY,Encoding::BINARY
446
+
447
+ while in_stream.read(@@block_size, buf)
448
+ out_stream << cipher.update(buf)
449
+ end
450
+ out_stream << cipher.final
451
+
452
+ nil
453
+ end
337
454
  #
338
455
  # Creates a cipher to encrypt data.
339
456
  #
metadata CHANGED
@@ -1,104 +1,77 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: file_pool
3
- version: !ruby/object:Gem::Version
4
- hash: 9
5
- prerelease:
6
- segments:
7
- - 0
8
- - 5
9
- - 1
10
- version: 0.5.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
11
5
  platform: ruby
12
- authors:
13
- - "robokopp (Robert Anni\xC3\xA9s)"
14
- autorequire:
6
+ authors:
7
+ - robokopp (Robert Anniés)
8
+ autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2018-03-26 00:00:00 +02:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
22
14
  name: uuidtools
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- hash: 15
30
- segments:
31
- - 2
32
- - 1
33
- - 2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
34
19
  version: 2.1.2
35
20
  type: :runtime
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: pry
39
21
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
43
31
  - - ">="
44
- - !ruby/object:Gem::Version
45
- hash: 3
46
- segments:
47
- - 0
48
- version: "0"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
49
34
  type: :development
50
- version_requirements: *id002
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
51
41
  description: |
52
42
  FilePool helps to manage a large number of files in a Ruby
53
43
  project. It takes care of the storage of files in a balanced directory
54
44
  tree and generates unique identifiers for all files.
55
-
56
- email:
45
+ email:
57
46
  - robokopp@fernwerk.net
58
47
  executables: []
59
-
60
48
  extensions: []
61
-
62
- extra_rdoc_files:
49
+ extra_rdoc_files:
50
+ - README.md
51
+ files:
63
52
  - README.md
64
- files:
65
53
  - lib/file_pool.rb
66
54
  - lib/file_pool/version.rb
67
- - README.md
68
- has_rdoc: true
69
55
  homepage: https://github.com/robokopp/file_pool
70
56
  licenses: []
71
-
72
- post_install_message:
57
+ metadata: {}
58
+ post_install_message:
73
59
  rdoc_options: []
74
-
75
- require_paths:
60
+ require_paths:
76
61
  - lib
77
- required_ruby_version: !ruby/object:Gem::Requirement
78
- none: false
79
- requirements:
80
- - - ~>
81
- - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
84
- - 2
85
- - 0
86
- version: "2.0"
87
- required_rubygems_version: !ruby/object:Gem::Requirement
88
- none: false
89
- requirements:
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
90
69
  - - ">="
91
- - !ruby/object:Gem::Version
92
- hash: 3
93
- segments:
94
- - 0
95
- version: "0"
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
96
72
  requirements: []
97
-
98
- rubyforge_project:
99
- rubygems_version: 1.6.2
100
- signing_key:
101
- specification_version: 3
73
+ rubygems_version: 3.2.22
74
+ signing_key:
75
+ specification_version: 4
102
76
  summary: Manage a large number files in a pool
103
77
  test_files: []
104
-