file_pool 0.5.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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
-