lib/rmega/nodes/downloadable.rb in rmega-0.1.7 vs lib/rmega/nodes/downloadable.rb in rmega-0.2.0
- old
+ new
@@ -1,71 +1,114 @@
-require 'rmega/pool'
-require 'rmega/utils'
-
module Rmega
module Nodes
module Downloadable
+ include Net
+ include Options
# 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.
def allocate(path)
- `dd if=/dev/zero of="#{path}" bs=1 count=0 seek=#{filesize} > /dev/null 2>&1`
- raise "Unable to create file #{path}" if ::File.size(path) != filesize
+ 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.open(path, 'r+b').tap { |f| f.rewind }
+ @file = ::File.open(path, 'r+b')
+ @file.rewind
end
+ def file_io_synchronize(&block)
+ @file_io_mutex ||= Mutex.new
+ @file_io_mutex.synchronize(&block)
+ end
+
+ def allocated?(path)
+ ::File.exists?(path) and ::File.size(path) == filesize
+ end
+
+ # Writes a buffer in the local file, starting from the start-n byte.
+ def write_chunk(start, buffer)
+ file_io_synchronize do
+ @file.seek(start)
+ @file.write(buffer)
+ end
+ end
+
+ 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
+
# Downloads a part of the remote file, starting from the start-n byte
# and ending after size-n bytes.
def download_chunk(start, size)
stop = start + size - 1
url = "#{storage_url}/#{start}-#{stop}"
- HTTPClient.new.get_content(url)
+
+ survive do
+ data = http_get_content(url)
+ raise("Unexpected data length") if data.size != size
+ return data
+ end
end
- # Writes a buffer in the local file, starting from the start-n byte.
- def write_chunk(file, start, buffer)
- file.seek(start)
- file.write(buffer)
+ 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
- def decrypt_chunk(start, encrypted_buffer)
- k = decrypted_file_key
- nonce = [k[4], k[5], (start/0x1000000000) >> 0, (start/0x10) >> 0]
- decrypt_key = [k[0] ^ k[4], k[1] ^ k[5], k[2] ^ k[6], k[3] ^ k[7]]
- Crypto::AesCtr.decrypt(decrypt_key, nonce, encrypted_buffer)[:data]
+ def calculate_chunck_mac(data)
+ mac_iv = @node_key.ctr_nonce * 2
+ return aes_cbc_mac(@node_key.aes_key, data, mac_iv)
end
def download(path)
path = ::File.expand_path(path)
path = Dir.exists?(path) ? ::File.join(path, name) : path
- logger.info "Download #{name} (#{filesize} bytes) => #{path}"
-
+ progress = Progress.new(filesize, caption: 'Download')
pool = Pool.new
- write_mutex = Mutex.new
- file = allocate(path)
- progress = Progress.new(total: filesize, caption: 'Download')
+ @resumed_download = allocated?(path)
+ allocate(path)
+ @node_key = NodeKey.load(decrypted_file_key)
- Utils.chunks(filesize).each do |start, size|
- pool.defer do
- encrypted_buffer = download_chunk(start, size)
+ chunk_macs = {}
- write_mutex.synchronize do
- clean_buffer = decrypt_chunk(start, encrypted_buffer)
+ 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)
- write_chunk(file, start, clean_buffer)
end
end
end
# waits for the last running threads to finish
- pool.wait_done
+ pool.shutdown
- file.flush
+ 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
+ @file.close rescue nil
end
end
end
end