Module: Rmega::Nodes::Downloadable
Instance Method Summary
collapse
Methods included from Options
included, #options
Methods included from Rmega::Net
#http_get_content, #http_post, #survive
Methods included from Loggable
included, #logger
Instance Method Details
#allocate(path) ⇒ Object
Creates the local file allocating filesize-n bytes (of /dev/zero) for it. Opens the local file to start writing from the beginning of it.
9
10
11
12
13
14
15
16
17
|
# File 'lib/rmega/nodes/downloadable.rb', line 9
def allocate(path)
unless allocated?(path)
`dd if=/dev/zero of="#{path}" bs=1 count=0 seek=#{filesize} > /dev/null 2>&1`
raise "Unable to allocate space for file #{path}" if ::File.size(path) != filesize
end
@file = ::File.open(path, 'r+b')
@file.rewind
end
|
#allocated?(path) ⇒ Boolean
24
25
26
|
# File 'lib/rmega/nodes/downloadable.rb', line 24
def allocated?(path)
::File.exists?(path) and ::File.size(path) == filesize
end
|
#calculate_chunck_mac(data) ⇒ Object
63
64
65
66
|
# File 'lib/rmega/nodes/downloadable.rb', line 63
def calculate_chunck_mac(data)
mac_iv = @node_key.ctr_nonce * 2
return aes_cbc_mac(@node_key.aes_key, data, mac_iv)
end
|
#decrypt_chunk(start, data) ⇒ Object
58
59
60
61
|
# File 'lib/rmega/nodes/downloadable.rb', line 58
def decrypt_chunk(start, data)
iv = @node_key.ctr_nonce + [start/0x1000000000, start/0x10].pack('l>*')
return aes_ctr_decrypt(@node_key.aes_key, data, iv)
end
|
#download(path) ⇒ Object
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
# File 'lib/rmega/nodes/downloadable.rb', line 68
def download(path)
path = ::File.expand_path(path)
path = Dir.exists?(path) ? ::File.join(path, name) : path
progress = Progress.new(filesize, caption: 'Download', filename: self.name)
pool = Pool.new
@resumed_download = allocated?(path)
allocate(path)
@node_key = NodeKey.load(decrypted_file_key)
chunk_macs = {}
each_chunk do |start, size|
pool.process do
data = @resumed_download ? read_chunk(start, size) : nil
if data
chunk_macs[start] = calculate_chunck_mac(data) if options.file_integrity_check
progress.increment(size, real: false)
else
data = decrypt_chunk(start, download_chunk(start, size))
chunk_macs[start] = calculate_chunck_mac(data) if options.file_integrity_check
write_chunk(start, data)
progress.increment(size)
end
end
end
pool.shutdown
if options.file_integrity_check
file_mac = aes_cbc_mac(@node_key.aes_key, chunk_macs.sort.map(&:last).join, "\x0"*16)
if Utils.compact_to_8_bytes(file_mac) != @node_key.meta_mac
raise("Checksum failed. File corrupted?")
end
end
return nil
ensure
@file.close rescue nil
end
|
#download_chunk(start, size) ⇒ Object
Downloads a part of the remote file, starting from the start-n byte and ending after size-n bytes.
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/rmega/nodes/downloadable.rb', line 47
def download_chunk(start, size)
stop = start + size - 1
url = "#{storage_url}/#{start}-#{stop}"
survive do
data = http_get_content(url)
raise("Unexpected data length") if data.size != size
return data
end
end
|
#file_io_synchronize(&block) ⇒ Object
19
20
21
22
|
# File 'lib/rmega/nodes/downloadable.rb', line 19
def file_io_synchronize(&block)
@file_io_mutex ||= Mutex.new
@file_io_mutex.synchronize(&block)
end
|
#read_chunk(start, size) ⇒ Object
36
37
38
39
40
41
42
43
|
# File 'lib/rmega/nodes/downloadable.rb', line 36
def read_chunk(start, size)
file_io_synchronize do
@file.seek(start)
data = @file.read(size)
@file.seek(start)
return (data == "\x0"*size) ? nil : data
end
end
|
#write_chunk(start, buffer) ⇒ Object
Writes a buffer in the local file, starting from the start-n byte.
29
30
31
32
33
34
|
# File 'lib/rmega/nodes/downloadable.rb', line 29
def write_chunk(start, buffer)
file_io_synchronize do
@file.seek(start)
@file.write(buffer)
end
end
|