module Rmega::Nodes::Downloadable
Public Instance Methods
allocate(path)
click to toggle source
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.
# 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)
click to toggle source
# File lib/rmega/nodes/downloadable.rb, line 24 def allocated?(path) ::File.exist?(path) and ::File.size(path) == filesize end
calculate_chunck_mac(data)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# File lib/rmega/nodes/downloadable.rb, line 68 def download(path) path = ::File.expand_path(path) path = Dir.exist?(path) ? ::File.join(path, name) : path progress = Progress.new(filesize, caption: 'Allocate', 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, caption: "Verify") 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, caption: "Download") end end end # waits for the last running threads to finish pool.wait_done 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)
click to toggle source
Downloads a part of the remote file, starting from the start-n byte and ending after size-n bytes.
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
Writes a buffer in the local file, starting from the start-n byte.
# 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