file_pool 0.5.2 → 0.6.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.
- data/lib/file_pool/version.rb +1 -1
- data/lib/file_pool.rb +168 -61
- metadata +5 -5
data/lib/file_pool/version.rb
CHANGED
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,48 @@ 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
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
+
if !@@copy_source and (File.stat(path).dev == File.stat(File.dirname(target)).dev)
|
96
|
+
FileUtils.link(path, target)
|
97
|
+
else
|
98
|
+
FileUtils.copy(path, target)
|
99
|
+
end
|
100
|
+
|
101
|
+
# don't chmod if orginal file is same as target (hard-linked)
|
102
|
+
if File.stat(orig_path).ino != File.stat(File.dirname(target)).ino
|
103
|
+
FileUtils.chmod(@@mode, target) if @@mode
|
104
|
+
FileUtils.chown(@@owner, @@group, target)
|
105
|
+
end
|
93
106
|
end
|
94
107
|
|
95
|
-
|
96
|
-
if
|
97
|
-
|
98
|
-
|
108
|
+
|
109
|
+
if options[:background]
|
110
|
+
# don't wait, avoid zombies
|
111
|
+
Process.detach(child)
|
112
|
+
else
|
113
|
+
# block until done
|
114
|
+
Process.waitpid(child)
|
99
115
|
end
|
100
116
|
|
101
117
|
newid
|
@@ -110,18 +126,45 @@ module FilePool
|
|
110
126
|
#
|
111
127
|
# source (String)::
|
112
128
|
# path of the file to add.
|
129
|
+
# options (Hash)::
|
130
|
+
# :background (true,false) adding large files can take long (esp. with encryption), +true+ won't block, default is +false+
|
113
131
|
#
|
114
132
|
# === Return Value:
|
115
133
|
#
|
116
134
|
# :: *String* containing a new unique ID for the file added.
|
117
135
|
# :: +false+ when the file could not be stored.
|
118
|
-
def self.add path
|
119
|
-
self.add!(path)
|
136
|
+
def self.add path, options = {}
|
137
|
+
self.add!(path, options)
|
120
138
|
|
121
139
|
rescue Exception
|
122
140
|
return false
|
123
141
|
end
|
124
142
|
|
143
|
+
#
|
144
|
+
# Add a new file from a stream to the file pool
|
145
|
+
#
|
146
|
+
def self.add_stream in_stream
|
147
|
+
newid = uuid
|
148
|
+
|
149
|
+
# ensure target path exists
|
150
|
+
if @@crypted_mode
|
151
|
+
FileUtils.mkpath(id2dir_secured newid)
|
152
|
+
else
|
153
|
+
FileUtils.mkpath(id2dir newid)
|
154
|
+
end
|
155
|
+
|
156
|
+
child = fork do
|
157
|
+
# create the target file to write
|
158
|
+
open(path(newid),'w') do |out_stream|
|
159
|
+
encrypt in_stream, out_stream
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
Process.detach(child)
|
164
|
+
|
165
|
+
newid
|
166
|
+
end
|
167
|
+
|
125
168
|
#
|
126
169
|
# Return the file's path corresponding to the passed file ID, no matter if it
|
127
170
|
# exists or not. In encrypting mode the file is first decrypted and the
|
@@ -141,7 +184,7 @@ module FilePool
|
|
141
184
|
#
|
142
185
|
# :: *String*, absolute path of the file in the pool or to temporary location if it was decrypted.
|
143
186
|
def self.path fid, options={}
|
144
|
-
options[:decrypt] =
|
187
|
+
options[:decrypt] = options.fetch(:decrypt, true)
|
145
188
|
|
146
189
|
raise InvalidFileId unless valid?(fid)
|
147
190
|
|
@@ -151,7 +194,7 @@ module FilePool
|
|
151
194
|
if @@crypted_mode
|
152
195
|
if options[:decrypt]
|
153
196
|
# return path of decrypted file (tmp path)
|
154
|
-
|
197
|
+
decrypt_to_tempfile id2dir_secured(fid) + "/#{fid}"
|
155
198
|
else
|
156
199
|
id2dir_secured(fid) + "/#{fid}"
|
157
200
|
end
|
@@ -171,6 +214,33 @@ module FilePool
|
|
171
214
|
end
|
172
215
|
end
|
173
216
|
|
217
|
+
# Returns an IO object providing an (unencrypted) stream of data of the given file ID
|
218
|
+
# To get the stream of the encrypted data pass :decrypt => false, as an option.
|
219
|
+
#
|
220
|
+
# === Parameters:
|
221
|
+
#
|
222
|
+
# fid (String)::
|
223
|
+
# File ID which was generated by a previous #add operation.
|
224
|
+
#
|
225
|
+
# options (Hash)::
|
226
|
+
# :decrypt (true,false) In encryption mode don't decrypt, but prioved the encrypted data. Defaults to +true+.
|
227
|
+
#
|
228
|
+
# === Return Value:
|
229
|
+
#
|
230
|
+
# :: *IO*, IO stream open for reading
|
231
|
+
#
|
232
|
+
def self.stream fid, options={}
|
233
|
+
options[:decrypt] = options.fetch(:decrypt, true)
|
234
|
+
|
235
|
+
if path = path(fid, :decrypt => false)
|
236
|
+
if @@crypted_mode and options[:decrypt]
|
237
|
+
decrypt_to_stream path
|
238
|
+
else
|
239
|
+
open(path)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
174
244
|
#
|
175
245
|
# Remove a previously added file by its ID. Same as FilePool.remove,
|
176
246
|
# but throws exceptions on failure.
|
@@ -271,7 +341,7 @@ module FilePool
|
|
271
341
|
end
|
272
342
|
|
273
343
|
#
|
274
|
-
# Crypt a file and store the result
|
344
|
+
# Crypt a file and store the result a Tempfile
|
275
345
|
#
|
276
346
|
# Returns the path to the crypted file.
|
277
347
|
#
|
@@ -282,30 +352,19 @@ module FilePool
|
|
282
352
|
#
|
283
353
|
# === Return Value:
|
284
354
|
#
|
285
|
-
# :: *String*Path and name of the crypted file.
|
286
|
-
def self.
|
355
|
+
# :: *String* Path and name of the crypted file.
|
356
|
+
def self.encrypt_to_tempfile path
|
287
357
|
# Crypt the file in the temp folder and copy after
|
288
|
-
|
289
|
-
|
358
|
+
tempfile = Tempfile.new 'FilePool-encrypt'
|
359
|
+
tempfile.sync = true
|
290
360
|
|
291
|
-
|
292
|
-
buf = ''.encode(Encoding::BINARY)
|
361
|
+
encrypt open(path), tempfile
|
293
362
|
|
294
|
-
|
295
|
-
while inf.read(@@block_size, buf)
|
296
|
-
result << cipher.update(buf)
|
297
|
-
result.flush
|
298
|
-
result.fsync
|
299
|
-
end
|
300
|
-
result << cipher.final
|
301
|
-
end
|
302
|
-
|
303
|
-
result.close
|
304
|
-
result.path
|
363
|
+
tempfile.path
|
305
364
|
end
|
306
365
|
|
307
366
|
#
|
308
|
-
# Decrypt
|
367
|
+
# Decrypt file to a file in /tmp
|
309
368
|
#
|
310
369
|
# Returns the path to the decrypted file.
|
311
370
|
#
|
@@ -316,28 +375,76 @@ module FilePool
|
|
316
375
|
#
|
317
376
|
# === Return Value:
|
318
377
|
#
|
319
|
-
# :: *String*Path and name of the
|
320
|
-
def self.
|
321
|
-
|
322
|
-
|
323
|
-
output = Tempfile.new 'FilePool-decrypt'
|
378
|
+
# :: *String* Path and name of the decrypted file.
|
379
|
+
def self.decrypt_to_tempfile path
|
380
|
+
tmpfile = Tempfile.new 'FilePool-decrypt'
|
381
|
+
tmpfile.sync = true
|
324
382
|
|
325
|
-
|
326
|
-
|
383
|
+
File.open(path) do |encrypted_file|
|
384
|
+
decrypt encrypted_file, tmpfile
|
385
|
+
end
|
386
|
+
|
387
|
+
tmpfile.path
|
388
|
+
end
|
389
|
+
|
390
|
+
#
|
391
|
+
# Decrypt file to a stream (IO) non-blocking by forking a child process.
|
392
|
+
#
|
393
|
+
# === Parameters:
|
394
|
+
#
|
395
|
+
# path (String)::
|
396
|
+
# path of the file to decrypt.
|
397
|
+
#
|
398
|
+
# === Return Value:
|
399
|
+
#
|
400
|
+
# :: *IO* decrypted data as stream
|
401
|
+
def self.decrypt_to_stream path
|
402
|
+
|
403
|
+
pipe_read, pipe_write = IO.pipe
|
404
|
+
|
405
|
+
child = fork do
|
406
|
+
# in child process: decrypting
|
407
|
+
pipe_read.close
|
327
408
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
output.flush
|
332
|
-
output.fsync
|
409
|
+
# open encrypted file
|
410
|
+
File.open(path) do |encrypted_file|
|
411
|
+
decrypt encrypted_file, pipe_write
|
333
412
|
end
|
334
|
-
|
413
|
+
|
414
|
+
pipe_write.close
|
335
415
|
end
|
336
416
|
|
337
|
-
|
338
|
-
|
417
|
+
# in parent
|
418
|
+
Process.detach(child) # not waiting for it
|
419
|
+
pipe_write.close
|
420
|
+
pipe_read
|
339
421
|
end
|
340
422
|
|
423
|
+
|
424
|
+
# decrypts data stream +in_stream+ (IO: readable) to +out+ (IO: writeable), blocking
|
425
|
+
def self.decrypt in_stream, out_stream
|
426
|
+
crypt(in_stream, out_stream, :decrypt)
|
427
|
+
end
|
428
|
+
|
429
|
+
# encrypts data stream +in+ (IO: readable) to +out+ (IO: writeable), blocking
|
430
|
+
def self.encrypt in_stream, out_stream
|
431
|
+
crypt(in_stream, out_stream, :encrypt)
|
432
|
+
end
|
433
|
+
|
434
|
+
def self.crypt in_stream, out_stream, direction
|
435
|
+
cipher = (direction == :encrypt) ? create_cipher : create_decipher
|
436
|
+
|
437
|
+
buf = ''.encode(Encoding::BINARY)
|
438
|
+
out_stream.set_encoding Encoding::BINARY,Encoding::BINARY
|
439
|
+
in_stream.set_encoding Encoding::BINARY,Encoding::BINARY
|
440
|
+
|
441
|
+
while in_stream.read(@@block_size, buf)
|
442
|
+
out_stream << cipher.update(buf)
|
443
|
+
end
|
444
|
+
out_stream << cipher.final
|
445
|
+
|
446
|
+
nil
|
447
|
+
end
|
341
448
|
#
|
342
449
|
# Creates a cipher to encrypt data.
|
343
450
|
#
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "robokopp (Robert Anni\xC3\xA9s)"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2019-02-12 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|